hyperledger/fabric · error

TickInterval cannot be zero

Error message

TickInterval cannot be zero

What it means

VerifyConfigMetadata guard: Options.TickInterval in the etcdraft ConfigMetadata is set to the zero value. The consensus options must provide a positive tick interval; a missing/zero value is rejected during channel config validation.

Source

Thrown at orderer/consensus/etcdraft/util.go:234

	if metadata.GetOptions().GetHeartbeatTick() == 0 ||
		metadata.GetOptions().GetElectionTick() == 0 ||
		metadata.GetOptions().GetMaxInflightBlocks() == 0 {
		// if SnapshotIntervalSize is zero, DefaultSnapshotIntervalSize is used
		return errors.Errorf("none of HeartbeatTick (%d), ElectionTick (%d) and MaxInflightBlocks (%d) can be zero",
			metadata.GetOptions().GetHeartbeatTick(), metadata.GetOptions().GetElectionTick(), metadata.GetOptions().GetMaxInflightBlocks())
	}

	// check Raft options
	if metadata.GetOptions().GetElectionTick() <= metadata.GetOptions().GetHeartbeatTick() {
		return errors.Errorf("ElectionTick (%d) must be greater than HeartbeatTick (%d)",
			metadata.GetOptions().GetElectionTick(), metadata.GetOptions().GetHeartbeatTick())
	}

	if d, err := time.ParseDuration(metadata.GetOptions().GetTickInterval()); err != nil {
		return errors.Errorf("failed to parse TickInterval (%s) to time duration: %s", metadata.GetOptions().GetTickInterval(), err)
	} else if d == 0 {
		return errors.Errorf("TickInterval cannot be zero")
	}

	if len(metadata.GetConsenters()) == 0 {
		return errors.Errorf("empty consenter set")
	}

	// verifying certificates for being signed by CA, expiration is ignored
	for _, consenter := range metadata.GetConsenters() {
		if consenter == nil {
			return errors.Errorf("metadata has nil consenter")
		}
		if err := validateConsenterTLSCerts(consenter, verifyOpts, true); err != nil {
			return errors.WithMessagef(err, "consenter %s:%d has invalid certificate", consenter.GetHost(), consenter.GetPort())
		}
	}

	if err := MetadataHasDuplication(metadata); err != nil {
		return err

View on GitHub (pinned to 2736b63f8f)

Solutions

  1. Set a positive TickInterval, e.g. "500ms".
  2. Validate that parsed duration > 0 before submitting the config update.
  3. Check profile in configtx.yaml and regenerate config metadata.

Example fix

// before
TickInterval: "0s"
// after
TickInterval: "500ms"
Defensive patterns

Strategy: validation

Validate before calling

d, err := time.ParseDuration(metadata.GetOptions().GetTickInterval())
if err != nil || d <= 0 {
	return errors.New("TickInterval must be a positive duration, e.g. 500ms")
}

Try / catch

if err := VerifyConfigMetadata(meta, opts); err != nil {
	if strings.Contains(err.Error(), "TickInterval cannot be zero") {
		return errors.New("configure a positive TickInterval in raft options")
	}
	return err
}

Prevention

When it happens

Trigger: Options.TickInterval set to "0", "0s", "0ms", or any string that parses to duration 0.

Common situations: Someone tried to 'disable' ticking by zeroing it; generated config leaving TickInterval as "0s"; template placeholder never filled in.

Related errors


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