hyperledger/fabric · error

chain is stopped

Error message

chain is stopped

What it means

WaitReady blocks until the chain can accept an operation by sending a nil onto submitC; if the chain has already stopped (doneC closed), the select picks doneC and returns 'chain is stopped'. Callers use it to wait for the chain to be ready to process requests, and this error tells them the chain is permanently halted.

Source

Thrown at orderer/consensus/etcdraft/chain.go:423

// Configure submits config type transactions for ordering.
func (c *Chain) Configure(env *common.Envelope, configSeq uint64) error {
	c.Metrics.ConfigProposalsReceived.Add(1)
	return c.Submit(&orderer.SubmitRequest{LastValidationSeq: configSeq, Payload: env, Channel: c.channelID}, 0)
}

// WaitReady blocks when the chain:
// - is catching up with other nodes using snapshot
//
// In any other case, it returns right away.
func (c *Chain) WaitReady() error {
	if err := c.isRunning(); err != nil {
		return err
	}

	select {
	case c.submitC <- nil:
	case <-c.doneC:
		return errors.Errorf("chain is stopped")
	}

	return nil
}

// Errored returns a channel that closes when the chain stops.
func (c *Chain) Errored() <-chan struct{} {
	c.errorCLock.RLock()
	defer c.errorCLock.RUnlock()
	return c.errorC
}

// Halt stops the chain.
func (c *Chain) Halt() {
	c.stop()
}

func (c *Chain) stop() bool {

View on GitHub (pinned to 2736b63f8f)

Solutions

  1. Detect the stopped chain and re-route submissions to another orderer in the consenter set.
  2. Check orderer logs for the reason the chain halted (e.g., 'Cowardly halting', channel removed) and fix the root cause.
  3. If the node's TLS/consenter certs were rotated, update the channel config and restart the orderer with correct certs so it can rejoin.
  4. If the channel was removed, re-join it via the participation API to restart the chain.
Defensive patterns

Strategy: try-catch

Try / catch

select {
case <-chain.Errored():
    // chain stopped; route work to another orderer
    return
default:
    if err := chain.WaitReady(); err != nil {
        if strings.Contains(err.Error(), "chain is stopped") {
            failoverToAnotherOrderer()
        }
    }
}

Prevention

When it happens

Trigger: Calling Chain.WaitReady (e.g., before Submit on the broadcast/deliver path) after the raft chain has halted — due to fatal errors, being removed from the channel, config-driven halting, or Orderer shutdown.

Common situations: Clients submitting transactions while the orderer is shutting down; the node cowardly halting after detecting a wrong raft ID (out-of-date certificate); channel removal triggering chain stop while traffic is in flight.

Related errors


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