hyperledger/fabric · info
chain stopped
Error message
chain stopped
What it means
ErrChainStopped is the sentinel error returned by the follower chain's pull loops (pullAfterJoin, pullUntilLatestWithRetry, pullUntilTarget) when the chain's stop channel is closed. It is a normal, expected shutdown signal rather than a fault — the follower was told to stop (e.g. channel removal or chain halt) while a pull/retry loop was waiting.
Source
Thrown at orderer/common/follower/follower_chain.go:27
import (
"bytes"
"sync"
"sync/atomic"
"time"
"github.com/hyperledger/fabric-lib-go/bccsp"
"github.com/hyperledger/fabric-lib-go/common/flogging"
"github.com/hyperledger/fabric-protos-go-apiv2/common"
"github.com/hyperledger/fabric/orderer/common/cluster"
"github.com/hyperledger/fabric/orderer/common/types"
"github.com/hyperledger/fabric/orderer/consensus"
"github.com/hyperledger/fabric/protoutil"
"github.com/pkg/errors"
"google.golang.org/protobuf/proto"
)
// ErrChainStopped is returned when the chain is stopped during execution.
var ErrChainStopped = errors.New("chain stopped")
//go:generate counterfeiter -o mocks/ledger_resources.go -fake-name LedgerResources . LedgerResources
// LedgerResources defines some interfaces of ledger & config resources needed by the follower.Chain.
type LedgerResources interface {
// ChannelID The channel ID.
ChannelID() string
// Block returns a block with the given number,
// or nil if such a block doesn't exist.
Block(number uint64) *common.Block
// Height returns the number of blocks in the chain this channel is associated with.
Height() uint64
// Append appends a new block to the ledger in its raw form.
Append(block *common.Block) error
}View on GitHub (pinned to 2736b63f8f)
Solutions
- No action needed — treat ErrChainStopped as a benign shutdown sentinel; check errors.Is(err, follower.ErrChainStopped) and ignore/log at debug
- If it appears unexpectedly, verify who called Halt on the follower chain (channel removal, orderer shutdown)
- Ensure caller code doesn't retry pulls after stop; stop loops instead of re-entering them
Example fix
// before
if err := c.pullUntilTarget(puller, target); err != nil {
c.logger.Panicf("Failed pulling to target: %v", err)
}
// after
if err := c.pullUntilTarget(puller, target); err != nil && !errors.Is(err, ErrChainStopped) {
c.logger.Errorf("Failed pulling to target: %v", err)
} Defensive patterns
Strategy: try-catch
Type guard
func isChainStopped(err error) bool { return errors.Is(err, follower.ErrChainStopped) } Try / catch
if err := chain.pullUntilTarget(puller, target); err != nil {
if errors.Is(err, follower.ErrChainStopped) {
logger.Debug("follower stopped during pull; shutting down cleanly")
return nil
}
return fmt.Errorf("pull failed: %w", err)
} Prevention
- Always compare with errors.Is(err, follower.ErrChainStopped) rather than string matching
- Do not restart pull loops after Halt; check stop state first
- Log at debug/info level, not error, for this sentinel
- Coordinate Halt calls with channel lifecycle to avoid unexpected stops
When it happens
Trigger: Stopping or removing a follower channel (chain.Halt / channel deactivation) while pullAfterJoin, pullUntilLatestWithRetry, or pullUntilTarget is blocked on timeAfter or height polling; the select's stopChan case wins and returns ErrChainStopped.
Common situations: Orderer shutdown with active followers; removing a follower channel after catching up; admin halting a chain during consensus relation changes; calling Halt concurrently with ongoing block pulls.
Related errors
- communication has been shut down
- communication has been shut down
- failed listing installed chaincodes
- failed to parse collection config
- [channel %s] failed to get chaincode container info for %s
AI-assisted analysis of hyperledger/fabric@2736b63f8f (2026-09-04).
Data as JSON: /api/errors/681becc91b857170.
Report an issue: GitHub.