hyperledger/fabric · error

nil ChaincodeEndorsedAction

Error message

nil ChaincodeEndorsedAction

What it means

The ChaincodeActionPayload deserialized successfully but its Action field (the ChaincodeEndorsedAction holding the proposal response payload and endorsements) is nil. Without an endorsed action there are no endorsements or results to validate, so the parser rejects the transaction.

Source

Thrown at core/tx/endorser/parser.go:83

	}

	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")
	}

	proposalResponsePayload, err := protoutil.UnmarshalProposalResponsePayload(
		ccActionPayload.Action.ProposalResponsePayload,
	)
	if err != nil {
		return nil, err
	}

	if len(proposalResponsePayload.Extension) == 0 {
		return nil, errors.New("nil Extension")
	}

	ccAction, err := protoutil.UnmarshalChaincodeAction(proposalResponsePayload.Extension)

View on GitHub (pinned to 2736b63f8f)

Solutions

  1. Set ChaincodeActionPayload.Action to a populated ChaincodeEndorsedAction (ProposalResponsePayload + Endorsements) before marshaling.
  2. Regenerate the transaction via the normal endorse/assemble flow so the endorsed action is always present.
  3. Inspect the producer of the payload for a code path that constructs it without Action.

Example fix

// before
ccPayload := &peer.ChaincodeActionPayload{Endorsements: endorsements} // Action missing
// after
ccPayload := &peer.ChaincodeActionPayload{
    Action: &peer.ChaincodeEndorsedAction{
        ProposalResponsePayload: prpBytes,
        Endorsements:            endorsements,
    },
}
Defensive patterns

Strategy: validation

Validate before calling

func hasEndorsedAction(ccPayloadBytes []byte) bool {
    p := &peer.ChaincodeActionPayload{}
    if proto.Unmarshal(ccPayloadBytes, p) != nil {
        return false
    }
    return p.GetAction() != nil
}

Type guard

func isEndorsed(p *peer.ChaincodeActionPayload) bool {
    return p.GetAction() != nil && len(p.GetAction().Endorsements) > 0
}

Try / catch

tx, err := parser.UnmarshalEndorserTxAndValidate(env)
if err != nil {
    if strings.Contains(err.Error(), "nil ChaincodeEndorsedAction") {
        // endorsed action missing: transaction has no endorsements, reject
        return ErrNoEndorsement
    }
    return err
}

Prevention

When it happens

Trigger: UnmarshalEndorserTxAndValidate is given a transaction whose ChaincodeActionPayload.Action is nil — the payload was marshaled with only Endorsements or Extension set, or Action was omitted entirely.

Common situations: Custom transaction assembly that fills ChaincodeActionPayload but forgets the Action sub-message; protobufs built by tooling versions that left Action unset; corrupted payloads that unmarshal partially.

Related errors


AI-assisted analysis of hyperledger/fabric@2736b63f8f (2026-09-04). Data as JSON: /api/errors/436e45d213c198cb. Report an issue: GitHub.