{"record":{"id":"186f869c7510e181","repo":"hyperledger/fabric","slug":"a-signed-proposal-is-required","errorCode":null,"errorMessage":"a signed proposal is required","messagePattern":"a signed proposal is required","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/pkg/gateway/evaluate.go","lineNumber":115,"sourceCode":"\t\t\t}\n\t\tcase <-ctx.Done():\n\t\t\t// Overall evaluation timeout expired\n\t\t\tlogger.Warnw(\"Evaluate call timed out while processing request\", \"channel\", request.GetChannelId(), \"txID\", request.GetTransactionId())\n\t\t\treturn nil, newRpcError(codes.DeadlineExceeded, \"evaluate timeout expired\")\n\t\t}\n\t}\n\n\tevaluateResponse := &gp.EvaluateResponse{\n\t\tResult: response,\n\t}\n\n\tlogger.Debugw(\"Evaluate call to endorser returned success\", \"channel\", request.GetChannelId(), \"txID\", request.GetTransactionId(), \"endorserAddress\", endorser.endpointConfig.address, \"endorserMspid\", endorser.endpointConfig.mspid, \"status\", response.GetStatus(), \"message\", response.GetMessage())\n\treturn evaluateResponse, nil\n}\n\nfunc getChannelAndChaincodeFromSignedProposal(signedProposal *peer.SignedProposal) (string, string, bool, error) {\n\tif len(signedProposal.GetProposalBytes()) == 0 {\n\t\treturn \"\", \"\", false, fmt.Errorf(\"a signed proposal is required\")\n\t}\n\tproposal, err := protoutil.UnmarshalProposal(signedProposal.GetProposalBytes())\n\tif err != nil {\n\t\treturn \"\", \"\", false, err\n\t}\n\theader, err := protoutil.UnmarshalHeader(proposal.GetHeader())\n\tif err != nil {\n\t\treturn \"\", \"\", false, err\n\t}\n\tchannelHeader, err := protoutil.UnmarshalChannelHeader(header.GetChannelHeader())\n\tif err != nil {\n\t\treturn \"\", \"\", false, err\n\t}\n\tpayload, err := protoutil.UnmarshalChaincodeProposalPayload(proposal.GetPayload())\n\tif err != nil {\n\t\treturn \"\", \"\", false, err\n\t}\n\tspec, err := protoutil.UnmarshalChaincodeInvocationSpec(payload.GetInput())","sourceCodeStart":97,"sourceCodeEnd":133,"githubUrl":"https://github.com/hyperledger/fabric/blob/2736b63f8fd5932511d56fe68b7039d15977f7f6/internal/pkg/gateway/evaluate.go#L97-L133","documentation":"Returned by getChannelAndChaincodeFromSignedProposal (internal/pkg/gateway/evaluate.go:115) when the signed proposal passed to Evaluate contains an empty ProposalBytes field. The gateway cannot parse a proposal with no payload, so it rejects the call before unmarshalling. It signals a malformed or unset signed proposal in the request.","triggerScenarios":"Calling Evaluate with a SignedProposal whose proposalBytes slice is empty — typically when the caller constructed the signed proposal themselves and left the serialized Proposal field unset.","commonSituations":"Manually building peer.SignedProposal without calling protoutil.Marshal on the proposal; forwarding a response proposal that was never populated; client SDK version mismatch producing an empty field.","solutions":["Verify the signed proposal passed to Evaluate has its proposal bytes populated (len > 0) before calling.","If building manually, serialize the proposal with protoutil.Marshal (or the appropriate SDK helper) into SignedProposal.ProposalBytes.","Use the SDK's proposal-generation APIs (NewSignedProposal / gateway.NewProposal) instead of constructing SignedProposal by hand.","Check that the bytes weren't lost in serialization between services (e.g. base64 decoding an empty string)."],"exampleFix":"// before\nsignedProposal := &peer.SignedProposal{} // empty proposalBytes\nresp, err := gw.Evaluate(ctx, signedProposal)\n// after\nproposalBytes, _ := protoutil.Marshal(proposal)\nsignedProposal := &peer.SignedProposal{ProposalBytes: proposalBytes, Signature: sig}\nresp, err := gw.Evaluate(ctx, signedProposal)","handlingStrategy":"validation","validationCode":"func validateSignedProposal(sp *peer.SignedProposal) error {\n    if sp == nil || len(sp.GetProposalBytes()) == 0 {\n        return errors.New(\"signed proposal has empty proposal bytes\")\n    }\n    return nil\n}","typeGuard":"func hasProposalBytes(sp *peer.SignedProposal) bool {\n    return sp != nil && len(sp.GetProposalBytes()) > 0\n}","tryCatchPattern":null,"preventionTips":["Always build signed proposals with protoutil.Marshal or SDK helpers, never with struct literals left empty.","Validate proposal bytes length before signing.","Keep client SDK and Fabric versions aligned.","Add unit tests asserting proposals are fully populated before submission."],"tags":["validation","fabric-gateway","proposal"],"backgroundTag":"missing-required-payload","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"}