hyperledger/fabric · error

epoch is non-zero

Error message

epoch is non-zero

What it means

The endorser requires the proposal's ChannelHeader.Epoch to be zero. Epoch is reserved for future reconfiguration semantics in Fabric and non-zero values are rejected to prevent replay/reconfiguration confusion. Any proposal carrying a non-zero epoch is invalid.

Source

Thrown at core/endorser/msgvalidation.go:143

		ChannelID: up.ChannelHeader.ChannelId,
		TxID:      up.TxID(),
	})

	// validate the header type
	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 {

View on GitHub (pinned to 2736b63f8f)

Solutions

  1. Leave ChannelHeader.Epoch as 0 (do not set it) when constructing proposals.
  2. If copying headers from other messages, explicitly zero the Epoch field.
  3. Use a supported Fabric SDK to construct proposals rather than hand-writing channel headers.

Example fix

// before
chdr := &common.ChannelHeader{Type: 1, TxId: txid, Epoch: 5}
// after
chdr := &common.ChannelHeader{Type: 1, TxId: txid, Epoch: 0}
Defensive patterns

Strategy: validation

Validate before calling

if hdr.Epoch != 0 {
    return errors.New("epoch must be 0 for endorsement proposals")
}

Type guard

func hasZeroEpoch(h *common.ChannelHeader) bool {
    return h != nil && h.Epoch == 0
}

Prevention

When it happens

Trigger: ProcessProposal -> preProcess -> Validate on a SignedProposal where up.ChannelHeader.Epoch != 0 — usually a manually built channel header with Epoch set from another system (e.g. Kafka epoch, view number).

Common situations: Custom client code that fills ChannelHeader fields generically and sets Epoch, ported code from other blockchain SDKs where 'epoch' means something else, mutated/replayed messages.

Related errors


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