{"record":{"id":"185d7a7e4f69036b","repo":"hyperledger/fabric","slug":"errchannelnotready","errorCode":"ErrChannelNotReady","errorMessage":"channel is not ready, he is a follower","messagePattern":"channel is not ready, he is a follower","errorType":"error_code","errorClass":null,"httpStatus":null,"severity":"error","filePath":"orderer/common/types/errors.go","lineNumber":37,"sourceCode":"// ErrChannelAlreadyExists is returned when trying to join a app channel that already exists (when the system channel does not\n// exist), or when trying to join the system channel when it already exists.\nvar ErrChannelAlreadyExists = errors.New(\"channel already exists\")\n\n// ErrAppChannelsAlreadyExists is returned when trying to join a system channel (that does not exist) when application channels\n// already exist.\nvar ErrAppChannelsAlreadyExists = errors.New(\"application channels already exist\")\n\n// ErrChannelNotExist is returned when trying to remove or list a channel that does not exist\nvar ErrChannelNotExist = errors.New(\"channel does not exist\")\n\n// ErrChannelPendingRemoval is returned when trying to remove or list a channel that is being removed.\nvar ErrChannelPendingRemoval = errors.New(\"channel pending removal\")\n\n// ErrChannelRemovalFailure is returned when a removal attempt failure has been recorded.\nvar ErrChannelRemovalFailure = errors.New(\"channel removal failure\")\n\n// ErrChannelNotReady is returned when trying to update a channel that is a follower\nvar ErrChannelNotReady = errors.New(\"channel is not ready, he is a follower\")\n","sourceCodeStart":19,"sourceCodeEnd":38,"githubUrl":"https://github.com/hyperledger/fabric/blob/2736b63f8fd5932511d56fe68b7039d15977f7f6/orderer/common/types/errors.go#L19-L38","documentation":"ErrChannelNotReady is returned by the channel participation API when an update is attempted on a channel whose local node is a follower in the consortium rather than a fully-started (active) participant. In Hyperledger Fabric's channel participation, only channels in the 'active' state can accept configuration updates via the REST API; follower channels are passively replicating and reject mutations. The error surfaces as HTTP 403 Forbidden from the UpdateChannel REST handler.","triggerScenarios":"Calling the channel participation REST API PUT /participation/v1/channels/{channelID} (UpdateChannel) when the channel's local state is 'follower' rather than 'active'. Also occurs in FetchBlock and sendUpdateError paths when the registrar reports the channel as a follower.","commonSituations":"An operator joins a channel with joinBySnapshot or as a follower before the node has caught up / been promoted to active, then tries to push a config update. Common after node restart before catch-up, or when updating a channel on a node that joined but never became a consenter/active member.","solutions":["Wait until the channel status becomes 'active' (check GET /participation/v1/channels/{channelID}) before issuing the update.","Verify the node was properly added as a consenter and that the join (or snapshot restore) completed; re-join the channel if it is stuck in follower state.","Ensure the config update is submitted to a node that is active in the channel, not a follower-only node.","If the channel is pending removal or stuck, remove and re-join the channel via the participation API."],"exampleFix":"// before: update immediately after join, channel still follower\ninfo, err := manager.UpdateChannel(channelID, envelope)\n\n// after: poll until active, then update\nfor {\n    info, _ := manager.FetchChannel(channelID)\n    if info.Status == types.StatusActive { break }\n    time.Sleep(time.Second)\n}\ninfo, err := manager.UpdateChannel(channelID, envelope)","handlingStrategy":"validation","validationCode":"info, err := manager.FetchChannel(channelID)\nif err != nil { return err }\nif info.Status != types.StatusActive {\n    return fmt.Errorf(\"channel %s not active (status=%s), refusing update\", channelID, info.Status)\n}","typeGuard":"func channelReadyForUpdate(info types.ChannelInfo) bool {\n    return info.Status == types.StatusActive\n}","tryCatchPattern":null,"preventionTips":["Always GET channel status before PUT updates","Automate joins and wait for status=active before config changes","Direct config updates at active consenter nodes only"],"tags":["orderer","channel-participation","rest-api","fabric"],"backgroundTag":"channel-not-active-for-update","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"}