hyperledger/fabric · error

only one action per transaction is supported, tx contains %d

Error message

only one action per transaction is supported, tx contains %d

What it means

Hyperledger Fabric v1.x supports exactly one action per endorser transaction. validateEndorserTransaction returns this error when the unmarshalled Transaction contains more or fewer than one TransactionAction, using errors.Errorf with the actual action count.

Source

Thrown at core/common/validation/msgvalidation.go:190

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

		// if the type is ENDORSER_TRANSACTION we unmarshal a SignatureHeader
		sHdr, err := protoutil.UnmarshalSignatureHeader(act.Header)
		if err != nil {
			return err
		}

		// validate the SignatureHeader - here we actually only
		// care about the nonce since the creator is in the outer header

View on GitHub (pinned to 2736b63f8f)

Solutions

  1. Build the transaction from exactly one set of collected proposal endorsements (one TransactionAction)
  2. If multiple proposal responses were concatenated, submit them as separate transactions
  3. Verify the SDK call (e.g. createTransaction with a single proposal response) rather than an array

Example fix

// before
tx.Actions = []*common.TransactionAction{action1, action2}
// after
tx.Actions = []*common.TransactionAction{action1} // one action per tx in v1.x
Defensive patterns

Strategy: validation

Validate before calling

if len(tx.Actions) != 1 { return fmt.Errorf("expected 1 action, got %d", len(tx.Actions)) }

Type guard

func isSingleActionTx(tx *common.Transaction) bool {
	return tx != nil && len(tx.Actions) == 1
}

Prevention

When it happens

Trigger: ValidateTransaction receiving a common.Transaction whose Actions slice length != 1 (0 actions, or 2+ actions concatenated from multiple proposals).

Common situations: Client SDKs (or custom code) merging multiple proposal responses into a single transaction; protobuf unions with Actions == nil (empty message); upgrades from formats that allowed multiple actions.

Related errors


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