hyperledger/fabric · error

none of HeartbeatTick (%d), ElectionTick (%d) and MaxInfligh

Error message

none of HeartbeatTick (%d), ElectionTick (%d) and MaxInflightBlocks (%d) can be zero

What it means

Raft timing relies on HeartbeatTick, ElectionTick and MaxInflightBlocks all being non-zero; a zero value would break leader election or flow control. VerifyConfigMetadata reports the current values of all three whenever any of them is zero.

Source

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

// VerifyConfigMetadata validates Raft config metadata.
// Note: ignores certificates expiration.
func VerifyConfigMetadata(metadata *etcdraft.ConfigMetadata, verifyOpts x509.VerifyOptions) error {
	if metadata == nil {
		// defensive check. this should not happen as CheckConfigMetadata
		// should always be called with non-nil config metadata
		return errors.Errorf("nil Raft config metadata")
	}

	if metadata.GetOptions() == nil {
		return errors.Errorf("nil Raft config metadata options")
	}

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

View on GitHub (pinned to 2736b63f8f)

Solutions

  1. Set all three fields to positive values (e.g. HeartbeatTick: 1, ElectionTick: 10, MaxInflightBlocks: 256) in the config metadata.
  2. Validate the metadata locally with VerifyConfigMetadata before submitting the config update.
  3. Re-run configtxgen with correct Raft profile settings and inspect the produced configtx.yaml.

Example fix

// before
Options{HeartbeatTick: 0, ElectionTick: 10, MaxInflightBlocks: 256}
// after
Options{HeartbeatTick: 1, ElectionTick: 10, MaxInflightBlocks: 256}
Defensive patterns

Strategy: validation

Validate before calling

o := metadata.GetOptions()
if o.GetHeartbeatTick() == 0 || o.GetElectionTick() == 0 || o.GetMaxInflightBlocks() == 0 {
	return errors.New("raft tick/inflight options must be positive")
}

Try / catch

if err := VerifyConfigMetadata(meta, opts); err != nil {
	if strings.Contains(err.Error(), "can be zero") {
		// parse reported values from message and surface them to the operator
		return fmt.Errorf("fix raft options in channel config: %v", err)
	}
	return err
}

Prevention

When it happens

Trigger: A channel config update sets ConsensusType metadata with any of Options.HeartbeatTick, Options.ElectionTick or Options.MaxInflightBlocks equal to 0.

Common situations: Typo in configtx.yaml consensus options; copying a template and deleting a field; constructing Options in Go code leaving fields at their zero value; partial migration of config between channels.

Related errors


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