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

  1. Populate ChaincodeEndorsedAction.ProposalResponsePayload with protoutil.MarshalOrPanic(&peer.ProposalResponsePayload{...}) before marshaling.
  2. Regenerate the transaction using the standard SDK flow where the proposal response payload is always attached.
  3. 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

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


AI-assisted analysis of hyperledger/fabric@2736b63f8f (2026-09-04). Data as JSON: /api/errors/c61d62f4bd6362f8. Report an issue: GitHub.