{"record":{"id":"e09587dced3f77a1","repo":"hyperledger/fabric","slug":"nil-extension","errorCode":null,"errorMessage":"nil Extension","messagePattern":"nil Extension","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"core/tx/endorser/parser.go","lineNumber":98,"sourceCode":"\t}\n\n\tif ccActionPayload.Action == nil {\n\t\treturn nil, errors.New(\"nil ChaincodeEndorsedAction\")\n\t}\n\n\tif len(ccActionPayload.Action.ProposalResponsePayload) == 0 {\n\t\treturn nil, errors.New(\"empty ProposalResponsePayload\")\n\t}\n\n\tproposalResponsePayload, err := protoutil.UnmarshalProposalResponsePayload(\n\t\tccActionPayload.Action.ProposalResponsePayload,\n\t)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tif len(proposalResponsePayload.Extension) == 0 {\n\t\treturn nil, errors.New(\"nil Extension\")\n\t}\n\n\tccAction, err := protoutil.UnmarshalChaincodeAction(proposalResponsePayload.Extension)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tcomputedTxID := protoutil.ComputeTxID(\n\t\ttxenv.SignatureHeader.Nonce,\n\t\ttxenv.SignatureHeader.Creator,\n\t)\n\n\treturn &EndorserTx{\n\t\tComputedTxID: computedTxID,\n\t\tChannelID:    txenv.ChannelHeader.ChannelId,\n\t\tCreator:      txenv.SignatureHeader.Creator,\n\t\tResponse:     ccAction.Response,\n\t\tEvents:       ccAction.Events,","sourceCodeStart":80,"sourceCodeEnd":116,"githubUrl":"https://github.com/hyperledger/fabric/blob/2736b63f8fd5932511d56fe68b7039d15977f7f6/core/tx/endorser/parser.go#L80-L116","documentation":"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.","triggerScenarios":"UnmarshalEndorserTxAndValidate receives a transaction where proposalResponsePayload.Extension has zero length — the ProposalResponsePayload was marshaled with ProposalHash only, omitting Extension.","commonSituations":"Endorser/proposal-response construction that skips ChaincodeAction marshaling; hand-built proposal responses in tests leaked into production; corrupted payloads missing the extension bytes.","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."],"exampleFix":"// before\nprp := &peer.ProposalResponsePayload{ProposalHash: hash} // Extension empty\n// after\nprp := &peer.ProposalResponsePayload{\n    ProposalHash: hash,\n    Extension:    protoutil.MarshalOrPanic(chaincodeAction),\n}","handlingStrategy":"validation","validationCode":"func hasChaincodeExtension(prpBytes []byte) bool {\n    prp := &peer.ProposalResponsePayload{}\n    if proto.Unmarshal(prpBytes, prp) != nil {\n        return false\n    }\n    return len(prp.GetExtension()) > 0\n}","typeGuard":"func hasExtension(prp *peer.ProposalResponsePayload) bool {\n    return prp != nil && len(prp.Extension) > 0\n}","tryCatchPattern":"tx, err := parser.UnmarshalEndorserTxAndValidate(env)\nif err != nil {\n    if strings.Contains(err.Error(), \"nil Extension\") {\n        // ChaincodeAction extension missing: reject or re-endorse\n        return ErrBadTransaction\n    }\n    return err\n}","preventionTips":["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."],"tags":["hyperledger-fabric","transaction","validation"],"backgroundTag":"malformed-envelope-payload","analyzedSha":"2736b63f8fd5932511d56fe68b7039d15977f7f6","analyzedAt":"2026-09-04T08:52:36.465Z","contentChangedAt":"2026-09-04T08:52:36.465Z","schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}