{"record":{"id":"d7bdf04a8a91c7ec","repo":"hyperledger/fabric","slug":"nil-transaction","errorCode":null,"errorMessage":"nil transaction","messagePattern":"nil transaction","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"core/common/validation/msgvalidation.go","lineNumber":181,"sourceCode":"// validateEndorserTransaction validates the payload of a\n// transaction assuming its type is ENDORSER_TRANSACTION\nfunc validateEndorserTransaction(data []byte, hdr *common.Header) error {\n\tputilsLogger.Debugf(\"validateEndorserTransaction starts for data %p, header %s\", data, hdr)\n\n\t// check for nil argument\n\tif data == nil || hdr == nil {\n\t\treturn errors.New(\"nil arguments\")\n\t}\n\n\t// if the type is ENDORSER_TRANSACTION we unmarshal a Transaction message\n\ttx, err := protoutil.UnmarshalTransaction(data)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\t// check for nil argument\n\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}","sourceCodeStart":163,"sourceCodeEnd":199,"githubUrl":"https://github.com/hyperledger/fabric/blob/2736b63f8fd5932511d56fe68b7039d15977f7f6/core/common/validation/msgvalidation.go#L163-L199","documentation":"After successfully unmarshalling the data bytes into a common.Transaction proto, validateEndorserTransaction checks that the resulting Transaction object is not nil. This catches the pathological case where the bytes unmarshal 'successfully' into an empty/nil message, which cannot carry any transaction actions.","triggerScenarios":"ValidateTransaction given data bytes that decode to an empty Transaction proto (e.g. zero-length or empty protobuf encoding); exercised directly by TestInvocationsBadArgs.","commonSituations":"Protobuf serialization bugs on the client side (signing an empty transaction), protobuf version mismatches producing empty messages, or marshalling a Transaction struct without setting Actions.","solutions":["Rebuild the transaction so it contains at least one TransactionAction before signing/submitting (tx.Actions != nil)","Check the client SDK serialization path that produced the data bytes","Add a pre-submission check that the marshalled transaction is non-empty"],"exampleFix":"// before\ntx := &common.Transaction{}\nraw, _ := proto.Marshal(tx)\n// after\ntx := &common.Transaction{Actions: []*common.TransactionAction{action}}\nraw, _ := proto.Marshal(tx)","handlingStrategy":"validation","validationCode":"tx, err := protoutil.UnmarshalTransaction(data)\nif err != nil || tx == nil || len(tx.Actions) == 0 { return errors.New(\"transaction empty or nil\") }","typeGuard":"func isNonEmptyTransaction(data []byte) bool {\n\ttx, err := protoutil.UnmarshalTransaction(data)\n\treturn err == nil && tx != nil\n}","tryCatchPattern":null,"preventionTips":["Never marshal a Transaction without at least one action","Add client-side sanity checks before signing","Pin protobuf runtime versions across client and peer"],"tags":["fabric","transaction-validation","nil-transaction"],"backgroundTag":"empty-protobuf-message","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"}