{"record":{"id":"abe03105f8853ec4","repo":"hyperledger/fabric","slug":"proposal-cannot-be-nil","errorCode":null,"errorMessage":"proposal cannot be nil","messagePattern":"proposal cannot be nil","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/peer/lifecycle/chaincode/common.go","lineNumber":50,"sourceCode":"\tDeliver(ctx context.Context, opts ...grpc.CallOption) (pb.Deliver_DeliverClient, error)\n\tDeliverFiltered(ctx context.Context, opts ...grpc.CallOption) (pb.Deliver_DeliverClient, error)\n}\n\n// Signer defines the interface needed for signing messages\ntype Signer interface {\n\tSign(msg []byte) ([]byte, error)\n\tSerialize() ([]byte, error)\n}\n\n// Writer defines the interface needed for writing a file\ntype Writer interface {\n\tWriteFile(string, string, []byte) error\n}\n\nfunc signProposal(proposal *pb.Proposal, signer Signer) (*pb.SignedProposal, error) {\n\t// check for nil argument\n\tif proposal == nil {\n\t\treturn nil, errors.New(\"proposal cannot be nil\")\n\t}\n\n\tif signer == nil {\n\t\treturn nil, errors.New(\"signer cannot be nil\")\n\t}\n\n\tproposalBytes, err := proto.Marshal(proposal)\n\tif err != nil {\n\t\treturn nil, errors.Wrap(err, \"error marshaling proposal\")\n\t}\n\n\tsignature, err := signer.Sign(proposalBytes)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\treturn &pb.SignedProposal{\n\t\tProposalBytes: proposalBytes,","sourceCodeStart":32,"sourceCodeEnd":68,"githubUrl":"https://github.com/hyperledger/fabric/blob/2736b63f8fd5932511d56fe68b7039d15977f7f6/internal/peer/lifecycle/chaincode/common.go#L32-L68","documentation":"signProposal in internal/peer/lifecycle/chaincode/common.go is a shared helper that serializes and signs a proposal before it is sent to the endorser. It refuses to run when the *pb.Proposal argument is nil, returning this error immediately before any marshaling or signing occurs. This indicates an internal construction bug: the caller built no proposal at all.","triggerScenarios":"Any of the lifecycle CLI commands (Approve, ReadinessCheck, Commit, Get, Install, Query) calls signProposal with a nil proposal, which happens only when the command's createInput/proposal-construction step failed to produce a proposal yet execution continued.","commonSituations":"Practically only seen from modified/forked code or test harnesses that invoke signProposal directly with nil; stock CLI commands always build a proposal first.","solutions":["Check upstream proposal construction (createInput/createCommand) for an ignored error that left the proposal nil","If calling signProposal directly (tests/tools), ensure the pb.Proposal is non-nil before invoking","Update to a stock fabric release where all callers validate the proposal before signing"],"exampleFix":"// before\nsp, err := signProposal(nil, signer)\n// after\nif proposal == nil {\n    return errors.New(\"no proposal to sign\")\n}\nsp, err := signProposal(proposal, signer)","handlingStrategy":"validation","validationCode":"if proposal == nil {\n    return errors.New(\"proposal must be constructed before signing\")\n}\nsp, err := signProposal(proposal, signer)","typeGuard":"func hasProposal(p *pb.Proposal) bool { return p != nil }","tryCatchPattern":"if err := run(); err != nil {\n    if strings.Contains(err.Error(), \"proposal cannot be nil\") {\n        // fix proposal construction upstream\n    }\n}","preventionTips":["Always build proposals via the library's createInput/createCommand helpers","Check errors from proposal-construction functions before proceeding","Add nil assertions in test harnesses that call signProposal directly"],"tags":["fabric","lifecycle","nil-argument"],"backgroundTag":"nil-argument","analyzedSha":"2736b63f8fd5932511d56fe68b7039d15977f7f6","analyzedAt":"2026-09-04T08:52:36.465Z","contentChangedAt":"2026-09-04T08:52:36.465Z","schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}