hyperledger/fabric · error
a signed proposal is required
Error message
a signed proposal is required
What it means
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.
Source
Thrown at internal/pkg/gateway/evaluate.go:115
}
case <-ctx.Done():
// Overall evaluation timeout expired
logger.Warnw("Evaluate call timed out while processing request", "channel", request.GetChannelId(), "txID", request.GetTransactionId())
return nil, newRpcError(codes.DeadlineExceeded, "evaluate timeout expired")
}
}
evaluateResponse := &gp.EvaluateResponse{
Result: response,
}
logger.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())
return evaluateResponse, nil
}
func getChannelAndChaincodeFromSignedProposal(signedProposal *peer.SignedProposal) (string, string, bool, error) {
if len(signedProposal.GetProposalBytes()) == 0 {
return "", "", false, fmt.Errorf("a signed proposal is required")
}
proposal, err := protoutil.UnmarshalProposal(signedProposal.GetProposalBytes())
if err != nil {
return "", "", false, err
}
header, err := protoutil.UnmarshalHeader(proposal.GetHeader())
if err != nil {
return "", "", false, err
}
channelHeader, err := protoutil.UnmarshalChannelHeader(header.GetChannelHeader())
if err != nil {
return "", "", false, err
}
payload, err := protoutil.UnmarshalChaincodeProposalPayload(proposal.GetPayload())
if err != nil {
return "", "", false, err
}
spec, err := protoutil.UnmarshalChaincodeInvocationSpec(payload.GetInput())View on GitHub (pinned to 2736b63f8f)
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).
Example fix
// before
signedProposal := &peer.SignedProposal{} // empty proposalBytes
resp, err := gw.Evaluate(ctx, signedProposal)
// after
proposalBytes, _ := protoutil.Marshal(proposal)
signedProposal := &peer.SignedProposal{ProposalBytes: proposalBytes, Signature: sig}
resp, err := gw.Evaluate(ctx, signedProposal) Defensive patterns
Strategy: validation
Validate before calling
func validateSignedProposal(sp *peer.SignedProposal) error {
if sp == nil || len(sp.GetProposalBytes()) == 0 {
return errors.New("signed proposal has empty proposal bytes")
}
return nil
} Type guard
func hasProposalBytes(sp *peer.SignedProposal) bool {
return sp != nil && len(sp.GetProposalBytes()) > 0
} Prevention
- 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.
When it happens
Trigger: 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.
Common situations: 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.
Related errors
- no channel id provided
- no chaincode spec is provided, channel id [%s]
- no chaincode id is provided, channel id [%s]
- no chaincode name is provided, channel id [%s]
- %s is mandatory and cannot be empty
AI-assisted analysis of hyperledger/fabric@2736b63f8f (2026-09-04).
Data as JSON: /api/errors/186f869c7510e181.
Report an issue: GitHub.