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

In HandleChain, when the orderer has no system channel, a chain must start as a follower that discovers its identity from the channel's config block; only then can detectSelfID map the local TLS/identity cert to a consenter ID. If detectSelfID fails, this wrapper error is returned, indicating the follower/chain-bootstrapping path did not produce a recognizable local consenter certificate in the channel config.

Source

Thrown at orderer/consensus/smartbft/consenter.go:186

	}
	if smartBFTChain, isBFTSmart := cs.Chain.(*BFTChain); isBFTSmart {
		return smartBFTChain
	}
	c.Logger.Warningf("Chain %s is of type %v and not smartbft.Chain", channelID, reflect.TypeOf(cs.Chain))
	return nil
}

// HandleChain returns a new Chain instance or an error upon failure
func (c *Consenter) HandleChain(support consensus.ConsenterSupport, metadata *cb.Metadata) (consensus.Chain, error) {
	consenters := support.SharedConfig().Consenters()
	configOptions, err := createSmartBftConfig(support.SharedConfig())
	if err != nil {
		return nil, err
	}

	selfID, err := c.detectSelfID(consenters)
	if err != nil {
		return nil, errors.Wrap(err, "without a system channel, a follower should have been created")
	}
	c.Logger.Infof("Local consenter id is %d", selfID)

	config, err := util.ConfigFromMetadataOptions(uint64(selfID), configOptions)
	if err != nil {
		return nil, errors.Wrap(err, "failed parsing smartbft configuration")
	}
	c.Logger.Debugf("SmartBFT-Go config: %+v", config)

	configValidator := &ConfigBlockValidator{
		ValidatingChannel:    support.ChannelID(),
		Filters:              c.Registrar,
		ConfigUpdateProposer: c.Registrar,
		Logger:               c.Logger,
	}

	egressCommFactory := func(runtimeConfig *atomic.Value, channelId string, comm cluster.Communicator) EgressComm {
		channelDecorator := zap.String("channel", channelId)

View on GitHub (pinned to 2736b63f8f)

Solutions

  1. Verify the orderer's General.TLS.Certificate and Cluster.ServerCertificate match a consenter entry (client_cert/server_cert) in the channel's Orderer.ConsenterMapping
  2. Re-issue the channel join with the correct latest config block: osnadmin channel join --channel-id ... --config-block <latest>
  3. Rotate or re-enroll the orderer's TLS/identity certs so they match the channel config, then restart the orderer
  4. Check detectSelfID's underlying error in the wrapped message for which cert (server/cluster/TLS) failed to match
Defensive patterns

Strategy: validation

Validate before calling

// Before joining, confirm local TLS certs appear in the channel's consenter mapping
block := fetchLatestConfigBlock(channelID)
consenters := extractConsenterMapping(block)
local := loadCert(ordererTLSPath)
if !containsCert(consenters, local) {
    return fmt.Errorf("local TLS cert %s not in channel ConsenterMapping; update channel config first", ordererTLSPath)
}

Try / catch

// HandleChain failure
chain, err := consenter.HandleChain(support, nil)
if err != nil {
    if strings.Contains(err.Error(), "follower should have been created") {
        log.Fatalf("local certs not in channel config: %v", err) // fix certs/block, don't retry blindly
    }
    return err
}

Prevention

When it happens

Trigger: Channel participation mode (no system channel) where HandleChain is invoked for a channel whose most recent config block's Orderer.ConsenterMapping does not contain a consenter whose server/cluster TLS certs match the local node's certificates, or detectSelfID fails on invalid PEM/TLS material.

Common situations: Using `osnadmin channel join` with TLS certificates on disk that don't match the certs enrolled in the channel config; joining a channel before the node's certs were added to the consenters list; misconfigured General.TLS / Cluster settings in orderer.yaml; stale join block after cert rotation.

Related errors


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