hyperledger/fabric · error

nonce is empty

Error message

nonce is empty

What it means

Every endorsement proposal must carry a nonce in its SignatureHeader; the nonce is a random value combined with the creator to form a unique transaction ID and prevent replay. An empty nonce makes the proposal invalid.

Source

Thrown at core/endorser/msgvalidation.go:148

	switch common.HeaderType(up.ChannelHeader.Type) {
	case common.HeaderType_ENDORSER_TRANSACTION:
	case common.HeaderType_CONFIG:
		// The CONFIG transaction type has _no_ business coming to the propose API.
		// In fact, anything coming to the Propose API is by definition an endorser
		// transaction, so any other header type seems like it ought to be an error... oh well.

	default:
		return errors.Errorf("invalid header type %s", common.HeaderType(up.ChannelHeader.Type))
	}

	// ensure the epoch is 0
	if up.ChannelHeader.Epoch != 0 {
		return errors.Errorf("epoch is non-zero")
	}

	// ensure that there is a nonce
	if len(up.SignatureHeader.Nonce) == 0 {
		return errors.Errorf("nonce is empty")
	}

	// ensure that there is a creator
	if len(up.SignatureHeader.Creator) == 0 {
		return errors.New("creator is empty")
	}

	expectedTxID := protoutil.ComputeTxID(up.SignatureHeader.Nonce, up.SignatureHeader.Creator)
	if up.TxID() != expectedTxID {
		return errors.Errorf("incorrectly computed txid '%s' -- expected '%s'", up.TxID(), expectedTxID)
	}

	if up.SignedProposal.ProposalBytes == nil {
		return errors.Errorf("empty proposal bytes")
	}

	if up.SignedProposal.Signature == nil {
		return errors.Errorf("empty signature bytes")

View on GitHub (pinned to 2736b63f8f)

Solutions

  1. Generate a cryptographically random nonce (e.g. 24 bytes from crypto/rand) and set it in the SignatureHeader before signing.
  2. Use SDK proposal helpers (newProposal / ChaincodeProposalFactory) which generate the nonce automatically.
  3. Verify the serialized SignatureHeader used for signing matches the one embedded in the proposal.

Example fix

// before
shdr := &common.SignatureHeader{Creator: creator}
// after
nonce := make([]byte, 24)
rand.Read(nonce)
shdr := &common.SignatureHeader{Creator: creator, Nonce: nonce}
Defensive patterns

Strategy: validation

Validate before calling

if len(shdr.Nonce) == 0 {
    return errors.New("nonce required: generate 24 random bytes via crypto/rand")
}

Type guard

func hasNonce(sh *common.SignatureHeader) bool {
    return sh != nil && len(sh.Nonce) > 0
}

Prevention

When it happens

Trigger: ProcessProposal -> preProcess -> Validate on a SignedProposal whose SignatureHeader.Nonce is a zero-length byte slice — typically a manually assembled SignatureHeader without setting Nonce.

Common situations: Hand-rolled protobuf client code, test fixtures with empty signature headers, clients that deserialize a proposal and rebuild it dropping the nonce.

Related errors


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