hyperledger/fabric · error

nil Extension

Error message

nil Extension

What it means

The ProposalResponsePayload deserialized but its Extension field (which must contain the serialized ChaincodeAction: results, events, and response) is empty. Fabric places the ChaincodeAction in the extension; without it the parser cannot produce the transaction's results, so it rejects the transaction.

Source

Thrown at core/tx/endorser/parser.go:98

	}

	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
	}

	computedTxID := protoutil.ComputeTxID(
		txenv.SignatureHeader.Nonce,
		txenv.SignatureHeader.Creator,
	)

	return &EndorserTx{
		ComputedTxID: computedTxID,
		ChannelID:    txenv.ChannelHeader.ChannelId,
		Creator:      txenv.SignatureHeader.Creator,
		Response:     ccAction.Response,
		Events:       ccAction.Events,

View on GitHub (pinned to 2736b63f8f)

Solutions

  1. Set ProposalResponsePayload.Extension to protoutil.MarshalOrPanic(&peer.ChaincodeAction{...}) before creating the proposal response.
  2. Regenerate via the standard endorsement path so the extension is always populated.
  3. Check the producer (endorser or mock) for a code path omitting the ChaincodeAction.

Example fix

// before
prp := &peer.ProposalResponsePayload{ProposalHash: hash} // Extension empty
// after
prp := &peer.ProposalResponsePayload{
    ProposalHash: hash,
    Extension:    protoutil.MarshalOrPanic(chaincodeAction),
}
Defensive patterns

Strategy: validation

Validate before calling

func hasChaincodeExtension(prpBytes []byte) bool {
    prp := &peer.ProposalResponsePayload{}
    if proto.Unmarshal(prpBytes, prp) != nil {
        return false
    }
    return len(prp.GetExtension()) > 0
}

Type guard

func hasExtension(prp *peer.ProposalResponsePayload) bool {
    return prp != nil && len(prp.Extension) > 0
}

Try / catch

tx, err := parser.UnmarshalEndorserTxAndValidate(env)
if err != nil {
    if strings.Contains(err.Error(), "nil Extension") {
        // ChaincodeAction extension missing: reject or re-endorse
        return ErrBadTransaction
    }
    return err
}

Prevention

When it happens

Trigger: UnmarshalEndorserTxAndValidate receives a transaction where proposalResponsePayload.Extension has zero length — the ProposalResponsePayload was marshaled with ProposalHash only, omitting Extension.

Common situations: Endorser/proposal-response construction that skips ChaincodeAction marshaling; hand-built proposal responses in tests leaked into production; corrupted payloads missing the extension bytes.

Related errors


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