{"record":{"id":"186cab8390bb95cf","repo":"hyperledger/fabric","slug":"node-d-doesn-t-exist-in-channel-s-s-membership","errorCode":null,"errorMessage":"node %d doesn't exist in channel %s's membership","messagePattern":"node (.+?) doesn't exist in channel (.+?)'s membership","errorType":"error_code","errorClass":null,"httpStatus":null,"severity":"error","filePath":"orderer/common/cluster/comm.go","lineNumber":132,"sourceCode":"}\n\n// Remote obtains a RemoteContext linked to the destination node on the context\n// of a given channel\nfunc (c *Comm) Remote(channel string, id uint64) (*RemoteContext, error) {\n\tc.Lock.RLock()\n\tdefer c.Lock.RUnlock()\n\n\tif c.shutdown {\n\t\treturn nil, errors.New(\"communication has been shut down\")\n\t}\n\n\tmapping, exists := c.Chan2Members[channel]\n\tif !exists {\n\t\treturn nil, errors.Errorf(\"channel %s doesn't exist\", channel)\n\t}\n\tstub := mapping.ByID(id)\n\tif stub == nil {\n\t\treturn nil, errors.Errorf(\"node %d doesn't exist in channel %s's membership\", id, channel)\n\t}\n\n\tif stub.Active() {\n\t\treturn stub.RemoteContext, nil\n\t}\n\n\terr := stub.Activate(c.createRemoteContext(stub, channel))\n\tif err != nil {\n\t\treturn nil, errors.WithStack(err)\n\t}\n\treturn stub.RemoteContext, nil\n}\n\n// Configure configures the channel with the given RemoteNodes\nfunc (c *Comm) Configure(channel string, newNodes []RemoteNode) {\n\tc.Logger.Infof(\"Entering, channel: %s, nodes: %v\", channel, newNodes)\n\tdefer c.Logger.Infof(\"Exiting\")\n","sourceCodeStart":114,"sourceCodeEnd":150,"githubUrl":"https://github.com/hyperledger/fabric/blob/2736b63f8fd5932511d56fe68b7039d15977f7f6/orderer/common/cluster/comm.go#L114-L150","documentation":"Thrown by the orderer cluster Comm layer's Remote() when the channel exists in Chan2Members but no node with the given ID is present in that channel's membership mapping (mapping.ByID(id) returned nil). The channel is known locally, but the specific cluster node is not a member of it, so no remote stub can be produced.","triggerScenarios":"Calling Comm.Remote(channel, id) with a node ID that is not in the channel's configured member list, e.g. an ID from a different channel's membership, or an outdated membership view after config changes.","commonSituations":"Passing the wrong node ID (off-by-one or from another channel); membership cache is stale after adding/removing orderers; a caller iterates node IDs from a global list instead of the channel's own member list.","solutions":["Confirm the node ID exists in the channel's membership (check the channel's consenters/orderer endpoints configuration).","Refresh membership via Configure/JoinChannel so Chan2Members reflects the current channel membership.","Fix the caller to derive node IDs from the channel's own member list, not a global or cross-channel list.","If membership changed recently, restart or reconfigure the orderer so it picks up the updated channel config."],"exampleFix":"// before\nremote, err := comm.Remote(channel, nodeIDFromOtherChannel)\n// after\nif mapping.Contains(nodeID) { // verify ID is in this channel's membership\n    remote, err = comm.Remote(channel, nodeID)\n}","handlingStrategy":"validation","validationCode":"// Go: check the node ID is part of the channel's membership before Remote\nmembers, ok := comm.Chan2Members[channel]\nif !ok || members.ByID(id) == nil {\n    return fmt.Errorf(\"node %d is not a member of channel %q\", id, channel)\n}","typeGuard":"func nodeInChannel(c *cluster.Comm, channel string, id uint64) bool {\n    c.RLock()\n    defer c.RUnlock()\n    mapping, ok := c.Chan2Members[channel]\n    if !ok {\n        return false\n    }\n    return mapping.ByID(id) != nil\n}","tryCatchPattern":"remote, err := comm.Remote(channel, id)\nif err != nil {\n    if strings.Contains(err.Error(), \"doesn't exist in channel\") {\n        // refresh membership then retry, or fall back to another node\n        return comm.refreshMembershipAndRetry(channel, id)\n    }\n    return err\n}","preventionTips":["Derive node IDs from the channel's own member list, never from global or cross-channel lists.","Keep membership views fresh by re-running Configure after consenters change.","Log the channel and ID on failure so stale-membership bugs surface quickly.","After adding/removing orderers, verify each node's channel config includes the new consenters."],"tags":["fabric","orderer","cluster","membership","node-id"],"backgroundTag":"node-not-in-membership","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"}