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

  1. Add this orderer as a consenter to the channel config (with its exact current TLS server/client certs) via config update
  2. 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
  3. Ensure the orderer's local TLS cert files match the certs registered in the channel consenter list
  4. 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

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


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