hyperledger/fabric · error
nil action
Error message
nil action
What it means
The single TransactionAction present in the transaction is nil. This happens when the protobuf Transaction serialized a null action pointer in its Actions slice. The parser checks the element after selecting it to avoid a nil-pointer dereference downstream.
Source
Thrown at core/tx/endorser/parser.go:70
}
if len(txenv.Data) == 0 {
return nil, errors.New("nil payload data")
}
tx, err := protoutil.UnmarshalTransaction(txenv.Data)
if err != nil {
return nil, err
}
if len(tx.GetActions()) != 1 {
return nil, errors.Errorf("only one transaction action is supported, %d were present", len(tx.GetActions()))
}
txAction := tx.GetActions()[0]
if txAction == nil {
return nil, errors.New("nil action")
}
if len(txAction.Payload) == 0 {
return nil, errors.New("empty ChaincodeActionPayload")
}
ccActionPayload, err := protoutil.UnmarshalChaincodeActionPayload(txAction.Payload)
if err != nil {
return nil, err
}
if ccActionPayload.Action == nil {
return nil, errors.New("nil ChaincodeEndorsedAction")
}
if len(ccActionPayload.Action.ProposalResponsePayload) == 0 {
return nil, errors.New("empty ProposalResponsePayload")
}View on GitHub (pinned to 2736b63f8f)
Solutions
- Rebuild the transaction ensuring each TransactionAction is a fully populated struct before marshaling.
- Add a client-side check that tx.Actions[0] != nil before creating the envelope.
- Trace the code that produced the transaction and fix the nil pointer assignment.
Example fix
// before
var action *common.TransactionAction // left nil
tx.Actions = []*common.TransactionAction{action}
// after
action := &common.TransactionAction{Payload: chaincodeActionPayloadBytes}
tx.Actions = []*common.TransactionAction{action} Defensive patterns
Strategy: type-guard
Validate before calling
func hasPopulatedAction(tx *common.Transaction) bool {
actions := tx.GetActions()
return len(actions) > 0 && actions[0] != nil
} Type guard
func isNotNilAction(a *common.TransactionAction) bool {
return a != nil
} Try / catch
tx, err := parser.UnmarshalEndorserTxAndValidate(env)
if err != nil {
if strings.Contains(err.Error(), "nil action") {
// transaction was built with a nil action: reject/regenerate
return ErrBadTransaction
}
return err
} Prevention
- Never append pointers to tx.Actions without initializing them.
- Run a sanity check on the Transaction struct before marshaling.
- Add unit tests for the transaction builder covering fully populated actions.
When it happens
Trigger: UnmarshalEndorserTxAndValidate is given an envelope whose Transaction.Actions[0] deserialized to nil — e.g. an action pointer appended to the slice without being populated before marshaling.
Common situations: Hand-built transactions where append(tx.Actions, nil) or an uninitialized struct pointer was marshaled; bugs in custom transaction-generation tooling.
Related errors
- invalid chaincode ID
- invalid header type %s
- nil payload data
- only one transaction action is supported, %d were present
- empty ChaincodeActionPayload
AI-assisted analysis of hyperledger/fabric@2736b63f8f (2026-09-04).
Data as JSON: /api/errors/8608b2d733f445ae.
Report an issue: GitHub.