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
- Set ProposalResponsePayload.Extension to protoutil.MarshalOrPanic(&peer.ChaincodeAction{...}) before creating the proposal response.
- Regenerate via the standard endorsement path so the extension is always populated.
- 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
- Set ProposalResponsePayload.Extension to the marshaled ChaincodeAction.
- Build proposal responses with the standard endorser code, not ad-hoc structs.
- Ensure mocks used in tests are replaced in production pipelines.
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
- 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/e09587dced3f77a1.
Report an issue: GitHub.