{"record":{"id":"c3c66a846631f521","repo":"hyperledger/fabric","slug":"nil-action","errorCode":null,"errorMessage":"nil action","messagePattern":"nil action","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"core/common/validation/msgvalidation.go","lineNumber":198,"sourceCode":"\tif tx == nil {\n\t\treturn errors.New(\"nil transaction\")\n\t}\n\n\t// TODO: validate tx.Version\n\n\t// TODO: validate ChaincodeHeaderExtension\n\n\t// hlf version 1 only supports a single action per transaction\n\tif len(tx.Actions) != 1 {\n\t\treturn errors.Errorf(\"only one action per transaction is supported, tx contains %d\", len(tx.Actions))\n\t}\n\n\tputilsLogger.Debugf(\"validateEndorserTransaction info: there are %d actions\", len(tx.Actions))\n\n\tfor _, act := range tx.Actions {\n\t\t// check for nil argument\n\t\tif act == nil {\n\t\t\treturn errors.New(\"nil action\")\n\t\t}\n\n\t\t// if the type is ENDORSER_TRANSACTION we unmarshal a SignatureHeader\n\t\tsHdr, err := protoutil.UnmarshalSignatureHeader(act.Header)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\n\t\t// validate the SignatureHeader - here we actually only\n\t\t// care about the nonce since the creator is in the outer header\n\t\terr = validateSignatureHeader(sHdr)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\n\t\tputilsLogger.Debugf(\"validateEndorserTransaction info: signature header is valid\")\n\n\t\t// if the type is ENDORSER_TRANSACTION we unmarshal a ChaincodeActionPayload","sourceCodeStart":180,"sourceCodeEnd":216,"githubUrl":"https://github.com/hyperledger/fabric/blob/2736b63f8fd5932511d56fe68b7039d15977f7f6/core/common/validation/msgvalidation.go#L180-L216","documentation":"Inside the loop over tx.Actions, validateEndorserTransaction guards against a nil TransactionAction element. A nil entry cannot yield a SignatureHeader or ChaincodeAction, so validation aborts early with this error.","triggerScenarios":"ValidateTransaction on a Transaction whose Actions slice contains a nil element (allocated slice with empty slot); TestInvocationsBadArgs covers it directly.","commonSituations":"Manual protobuf construction (make([]*common.TransactionAction, 1) without assignment), partial deserialization, or client code that appends a placeholder action it never fills in.","solutions":["Ensure every element appended to tx.Actions is a fully populated, non-nil TransactionAction","Re-serialize the transaction from a known-good code path (SDK transaction builder)","Add a pre-submit loop asserting no nil actions before marshalling"],"exampleFix":"// before\nactions := make([]*common.TransactionAction, 2) // [nil, nil]\n// after\nactions := []*common.TransactionAction{action1, action2}","handlingStrategy":"validation","validationCode":"for i, act := range tx.Actions {\n\tif act == nil { return fmt.Errorf(\"action %d is nil\", i) }\n}","typeGuard":"func allActionsPresent(tx *common.Transaction) bool {\n\tfor _, a := range tx.Actions { if a == nil { return false } }\n\treturn len(tx.Actions) > 0\n}","tryCatchPattern":null,"preventionTips":["Use composite literals to fill slices instead of make() without assignment","Validate tx structure before marshalling","Prefer SDK transaction builders over manual proto assembly"],"tags":["fabric","transaction-validation","nil-argument"],"backgroundTag":"nil-argument","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"}