hyperledger/fabric · error
without a system channel, a follower should have been create
Error message
without a system channel, a follower should have been created
What it means
After building the consenter map, HandleChain (orderer/consensus/etcdraft/consenter.go:149) calls detectSelfID to find this orderer's TLS certificate among the channel consenters. If it is not found (and no system-channel-based follower/fetcher path applies), this error wraps the cause, indicating this node is not a consenter of the channel and, without a system channel to fall back on, it cannot serve or follow the channel.
Source
Thrown at orderer/consensus/etcdraft/consenter.go:149
c.Logger.Debugf("Block metadata is nil at block height=%d, it is consensus-type migration", support.Height())
}
// determine raft replica set mapping for each node to its id
// for newly started chain we need to read and initialize raft
// metadata by creating mapping between conseter and its id.
// In case chain has been restarted we restore raft metadata
// information from the recently committed block meta data
// field.
blockMetadata, err := ReadBlockMetadata(metadata, m)
if err != nil {
return nil, errors.Wrapf(err, "failed to read Raft metadata")
}
consenters := CreateConsentersMap(blockMetadata, m)
id, err := c.detectSelfID(consenters)
if err != nil {
return nil, errors.Wrap(err, "without a system channel, a follower should have been created")
}
var evictionSuspicion time.Duration
if c.EtcdRaftConfig.EvictionSuspicion == "" {
c.Logger.Infof("EvictionSuspicion not set, defaulting to %v", DefaultEvictionSuspicion)
evictionSuspicion = DefaultEvictionSuspicion
} else {
evictionSuspicion, err = time.ParseDuration(c.EtcdRaftConfig.EvictionSuspicion)
if err != nil {
c.Logger.Panicf("Failed parsing Consensus.EvictionSuspicion: %s: %v", c.EtcdRaftConfig.EvictionSuspicion, err)
}
}
var tickInterval time.Duration
if c.EtcdRaftConfig.TickIntervalOverride == "" {
tickInterval, err = time.ParseDuration(m.GetOptions().GetTickInterval())
if err != nil {
return nil, errors.Errorf("failed to parse TickInterval (%s) to time duration", m.GetOptions().GetTickInterval())View on GitHub (pinned to 2736b63f8f)
Solutions
- Add this orderer as a consenter to the channel config (with its exact current TLS server/client certs) via config update
- If the node was intentionally removed, stop it and remove the channel from its local configuration (or delete its channel ledger) so it does not retry
- Ensure the orderer's local TLS cert files match the certs registered in the channel consenter list
- Compare certificate PEMs byte-for-byte between the node's tls/server.crt and channel config metadata
Example fix
// before: node starts channel it isn't a member of
orderer3 not listed in channel Consenters
// after
configtxlator update: add {Host: orderer3.example.com, Port: 7050, server/client certs of orderer3} to channel ConfigMetadata, then restart Defensive patterns
Strategy: validation
Validate before calling
consenters := CreateConsentersMap(blockMetadata, configMetadata)
if _, err := detectSelfID(consenters); err != nil {
// this node is not a consenter: either add it via config update or remove the channel locally
return fmt.Errorf("node not in consenter set: %w", err)
} Type guard
func nodeIsConsenter(selfTLSCert []byte, consenters map[uint64]*common.Consenter) bool {
for _, c := range consenters {
if bytes.Equal(c.GetServerTlsCert(), selfTLSCert) { return true }
}
return false
} Try / catch
chain, err := consenter.HandleChain(support, metadata)
if err != nil {
if strings.Contains(err.Error(), "without a system channel, a follower should have been created") {
// check membership: add node to channel config or remove channel from this orderer
}
} Prevention
- Only join orderers to channels after their consenter entry is committed
- Keep local TLS certs in sync with the certs registered in channel config
- Remove evicted nodes from the channel locally so they stop retrying
When it happens
Trigger: An orderer whose local TLS certificate does not match any consenter cert in the channel's raft metadata attempts HandleChain; detectSelfID returns 'failed to detect own cluster membership' or 'was not found in consenter set'.
Common situations: Orderer joined the channel before its consenter entry was added via config update; TLS certificates regenerated so the running cert differs from the one in channel config; typo/mismatch between Orderer.TLS settings and the consenter cert in channel config; running without a system channel where membership eviction means the node must not serve the channel.
Related errors
- consenter %s:%d has invalid certificates
- duplicate consenter: server cert: %s, client cert: %s
- node %d doesn't exist in channel %s's membership
- No suitable BFT consenter for Raft consenter: %v
- failed to create x509 verify options from old and new ordere
AI-assisted analysis of hyperledger/fabric@2736b63f8f (2026-09-04).
Data as JSON: /api/errors/dfbcb0b42fb1f2cc.
Report an issue: GitHub.