hyperledger/fabric · error
empty ProposalResponsePayload
Error message
empty ProposalResponsePayload
What it means
The ChaincodeEndorsedAction exists but its ProposalResponsePayload bytes are empty. The ProposalResponsePayload carries the proposal hash, the chaincode result, and the response extension; without it the parser cannot extract the chaincode action or validate the read/write sets.
Source
Thrown at core/tx/endorser/parser.go:87
if txAction == nil {
return nil, errors.New("nil action")
}
if len(txAction.Payload) == 0 {
return nil, errors.New("empty ChaincodeActionPayload")
}
ccActionPayload, err := protoutil.UnmarshalChaincodeActionPayload(txAction.Payload)
if err != nil {
return nil, err
}
if ccActionPayload.Action == nil {
return nil, errors.New("nil ChaincodeEndorsedAction")
}
if len(ccActionPayload.Action.ProposalResponsePayload) == 0 {
return nil, errors.New("empty ProposalResponsePayload")
}
proposalResponsePayload, err := protoutil.UnmarshalProposalResponsePayload(
ccActionPayload.Action.ProposalResponsePayload,
)
if err != nil {
return nil, err
}
if len(proposalResponsePayload.Extension) == 0 {
return nil, errors.New("nil Extension")
}
ccAction, err := protoutil.UnmarshalChaincodeAction(proposalResponsePayload.Extension)
if err != nil {
return nil, err
}
View on GitHub (pinned to 2736b63f8f)
Solutions
- Populate ChaincodeEndorsedAction.ProposalResponsePayload with protoutil.MarshalOrPanic(&peer.ProposalResponsePayload{...}) before marshaling.
- Regenerate the transaction using the standard SDK flow where the proposal response payload is always attached.
- Verify byte integrity of the envelope along its storage/transport path.
Example fix
// before
action := &peer.ChaincodeEndorsedAction{Endorsements: endorsements} // PRP missing
// after
action := &peer.ChaincodeEndorsedAction{
ProposalResponsePayload: protoutil.MarshalOrPanic(prp),
Endorsements: endorsements,
} Defensive patterns
Strategy: validation
Validate before calling
func hasProposalResponsePayload(action *peer.ChaincodeEndorsedAction) bool {
return action != nil && len(action.ProposalResponsePayload) > 0
} Type guard
func hasPRP(a *peer.ChaincodeEndorsedAction) bool {
return a != nil && len(a.ProposalResponsePayload) > 0
} Try / catch
tx, err := parser.UnmarshalEndorserTxAndValidate(env)
if err != nil {
if strings.Contains(err.Error(), "empty ProposalResponsePayload") {
// proposal response payload missing: regenerate from endorsement
return ErrBadTransaction
}
return err
} Prevention
- Always attach marshaled ProposalResponsePayload to the endorsed action.
- Copy endorsements together with their payload, never in isolation.
- Check for byte truncation across serialization boundaries.
When it happens
Trigger: UnmarshalEndorserTxAndValidate receives a transaction where ccActionPayload.Action.ProposalResponsePayload is a zero-length byte slice — the endorsed action was constructed without the proposal response payload.
Common situations: Transaction assembly skipping the proposal-response-payload marshaling step; endorsements copied without their payload; payload truncation in transit or storage.
Related errors
- invalid chaincode ID
- invalid header type %s
- nil payload data
- only one transaction action is supported, %d were present
- nil action
AI-assisted analysis of hyperledger/fabric@2736b63f8f (2026-09-04).
Data as JSON: /api/errors/c61d62f4bd6362f8.
Report an issue: GitHub.