hyperledger/fabric · error
nil transaction
Error message
nil transaction
What it means
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.
Source
Thrown at core/common/validation/msgvalidation.go:181
// validateEndorserTransaction validates the payload of a
// transaction assuming its type is ENDORSER_TRANSACTION
func validateEndorserTransaction(data []byte, hdr *common.Header) error {
putilsLogger.Debugf("validateEndorserTransaction starts for data %p, header %s", data, hdr)
// check for nil argument
if data == nil || hdr == nil {
return errors.New("nil arguments")
}
// if the type is ENDORSER_TRANSACTION we unmarshal a Transaction message
tx, err := protoutil.UnmarshalTransaction(data)
if err != nil {
return err
}
// check for nil argument
if tx == nil {
return errors.New("nil transaction")
}
// TODO: validate tx.Version
// TODO: validate ChaincodeHeaderExtension
// hlf version 1 only supports a single action per transaction
if len(tx.Actions) != 1 {
return errors.Errorf("only one action per transaction is supported, tx contains %d", len(tx.Actions))
}
putilsLogger.Debugf("validateEndorserTransaction info: there are %d actions", len(tx.Actions))
for _, act := range tx.Actions {
// check for nil argument
if act == nil {
return errors.New("nil action")
}View on GitHub (pinned to 2736b63f8f)
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
Example fix
// before
tx := &common.Transaction{}
raw, _ := proto.Marshal(tx)
// after
tx := &common.Transaction{Actions: []*common.TransactionAction{action}}
raw, _ := proto.Marshal(tx) Defensive patterns
Strategy: validation
Validate before calling
tx, err := protoutil.UnmarshalTransaction(data)
if err != nil || tx == nil || len(tx.Actions) == 0 { return errors.New("transaction empty or nil") } Type guard
func isNonEmptyTransaction(data []byte) bool {
tx, err := protoutil.UnmarshalTransaction(data)
return err == nil && tx != nil
} Prevention
- Never marshal a Transaction without at least one action
- Add client-side sanity checks before signing
- Pin protobuf runtime versions across client and peer
When it happens
Trigger: ValidateTransaction given data bytes that decode to an empty Transaction proto (e.g. zero-length or empty protobuf encoding); exercised directly by TestInvocationsBadArgs.
Common situations: 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.
Related errors
- error unmarshalling ChaincodeHeaderExtension
- nil ChaincodeId in header extension
- nil ChaincodeId in ChaincodeAction
- invalid chaincode ID
- inconsistent ccid info (%s/%s)
AI-assisted analysis of hyperledger/fabric@2736b63f8f (2026-09-04).
Data as JSON: /api/errors/d7bdf04a8a91c7ec.
Report an issue: GitHub.