hyperledger/fabric · error
empty consenter set
Error message
empty consenter set
What it means
A Raft channel must have at least one consenter (ordering node). VerifyConfigMetadata returns this error when metadata.GetConsenters() is empty, since a cluster with zero nodes cannot elect a leader or replicate the ledger.
Source
Thrown at orderer/consensus/etcdraft/util.go:238
// 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
}
return nil
}View on GitHub (pinned to 2736b63f8f)
Solutions
- Include at least one Consenter (host, port, client/server TLS certs) in ConfigMetadata.
- If removing nodes, keep the remaining set non-empty and follow orderly consenter-exit procedures.
- Regenerate config via configtxgen with the Raft orderer profiles listing the orderers.
Example fix
// before
Consenters: []*Consenter{}
// after
Consenters: []*Consenter{{Host: "orderer.example.com", Port: 7050, ClientTlsCert: c, ServerTlsCert: s}} Defensive patterns
Strategy: validation
Validate before calling
if len(metadata.GetConsenters()) == 0 {
return errors.New("channel config must define at least one raft consenter")
} Try / catch
if err := VerifyConfigMetadata(meta, opts); err != nil {
if strings.Contains(err.Error(), "empty consenter set") {
return errors.New("cannot remove all ordering nodes from a raft channel")
}
return err
} Prevention
- When removing orderers, always leave at least one (preferably a majority quorum) running.
- Keep consenters lists in configtx.yaml profiles in sync with the live orderer set.
- Dry-run config updates through VerifyConfigMetadata before submission.
When it happens
Trigger: A config update removes all consenters, or a ConfigMetadata is constructed with only Options but no Consenter entries.
Common situations: Attempting to shrink an ordering service to zero nodes; hand-built metadata for tests; consenters list dropped during channel-create config generation.
Related errors
- cannot marshal metadata for orderer type %s: %s
- could not read config update
- nil Raft config metadata options
- none of HeartbeatTick (%d), ElectionTick (%d) and MaxInfligh
- ElectionTick (%d) must be greater than HeartbeatTick (%d)
AI-assisted analysis of hyperledger/fabric@2736b63f8f (2026-09-04).
Data as JSON: /api/errors/5505f997262cfa0a.
Report an issue: GitHub.