{"record":{"id":"681becc91b857170","repo":"hyperledger/fabric","slug":"chain-stopped","errorCode":null,"errorMessage":"chain stopped","messagePattern":"chain stopped","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"info","filePath":"orderer/common/follower/follower_chain.go","lineNumber":27,"sourceCode":"import (\n\t\"bytes\"\n\t\"sync\"\n\t\"sync/atomic\"\n\t\"time\"\n\n\t\"github.com/hyperledger/fabric-lib-go/bccsp\"\n\t\"github.com/hyperledger/fabric-lib-go/common/flogging\"\n\t\"github.com/hyperledger/fabric-protos-go-apiv2/common\"\n\t\"github.com/hyperledger/fabric/orderer/common/cluster\"\n\t\"github.com/hyperledger/fabric/orderer/common/types\"\n\t\"github.com/hyperledger/fabric/orderer/consensus\"\n\t\"github.com/hyperledger/fabric/protoutil\"\n\t\"github.com/pkg/errors\"\n\t\"google.golang.org/protobuf/proto\"\n)\n\n// ErrChainStopped is returned when the chain is stopped during execution.\nvar ErrChainStopped = errors.New(\"chain stopped\")\n\n//go:generate counterfeiter -o mocks/ledger_resources.go -fake-name LedgerResources . LedgerResources\n\n// LedgerResources defines some interfaces of ledger & config resources needed by the follower.Chain.\ntype LedgerResources interface {\n\t// ChannelID The channel ID.\n\tChannelID() string\n\n\t// Block returns a block with the given number,\n\t// or nil if such a block doesn't exist.\n\tBlock(number uint64) *common.Block\n\n\t// Height returns the number of blocks in the chain this channel is associated with.\n\tHeight() uint64\n\n\t// Append appends a new block to the ledger in its raw form.\n\tAppend(block *common.Block) error\n}","sourceCodeStart":9,"sourceCodeEnd":45,"githubUrl":"https://github.com/hyperledger/fabric/blob/2736b63f8fd5932511d56fe68b7039d15977f7f6/orderer/common/follower/follower_chain.go#L9-L45","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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"],"exampleFix":"// before\nif err := c.pullUntilTarget(puller, target); err != nil {\n    c.logger.Panicf(\"Failed pulling to target: %v\", err)\n}\n// after\nif err := c.pullUntilTarget(puller, target); err != nil && !errors.Is(err, ErrChainStopped) {\n    c.logger.Errorf(\"Failed pulling to target: %v\", err)\n}","handlingStrategy":"try-catch","validationCode":null,"typeGuard":"func isChainStopped(err error) bool { return errors.Is(err, follower.ErrChainStopped) }","tryCatchPattern":"if err := chain.pullUntilTarget(puller, target); err != nil {\n    if errors.Is(err, follower.ErrChainStopped) {\n        logger.Debug(\"follower stopped during pull; shutting down cleanly\")\n        return nil\n    }\n    return fmt.Errorf(\"pull failed: %w\", err)\n}","preventionTips":["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"],"tags":["lifecycle","shutdown","follower"],"backgroundTag":"chain-stopped","analyzedSha":"2736b63f8fd5932511d56fe68b7039d15977f7f6","analyzedAt":"2026-09-04T08:52:36.465Z","contentChangedAt":"2026-09-04T08:52:36.465Z","schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}