hyperledger/fabric · error

node %d doesn't exist in channel %s's membership

Error message

node %d doesn't exist in channel %s's membership

What it means

AuthCommMgr.Remote() found the channel's membership mapping but the requested node ID has no stub in it (mapping.ByID(id) returned nil). This means the node with that consenter ID is not part of the channel's current membership as known to this orderer.

Source

Thrown at orderer/common/cluster/commauth.go:57

	NodeIdentity   []byte
	Signer         identity.Signer
}

func (ac *AuthCommMgr) Remote(channel string, id uint64) (*RemoteContext, error) {
	ac.Lock.RLock()
	defer ac.Lock.RUnlock()

	if ac.shutdown {
		return nil, errors.New("communication has been shut down")
	}

	mapping, exists := ac.Chan2Members[channel]
	if !exists {
		return nil, errors.Errorf("channel %s doesn't exist", channel)
	}
	stub := mapping.ByID(id)
	if stub == nil {
		return nil, errors.Errorf("node %d doesn't exist in channel %s's membership", id, channel)
	}

	if stub.Active() {
		return stub.RemoteContext, nil
	}

	err := stub.Activate(ac.createRemoteContext(stub, channel))
	if err != nil {
		return nil, errors.WithStack(err)
	}
	return stub.RemoteContext, nil
}

func (ac *AuthCommMgr) Configure(channel string, members []RemoteNode) {
	ac.Logger.Infof("Configuring communication module for Channel: %s with nodes:%v", channel, members)

	ac.Lock.Lock()
	defer ac.Lock.Unlock()

View on GitHub (pinned to 2736b63f8f)

Solutions

  1. Verify the node ID is a valid consenter in the channel's current config (check the orderer's channel config / configtx consenter list).
  2. Re-run Configure(channel, members) with the current channel membership so removed/new consenters are reflected.
  3. After any channel reconfiguration, invalidate any cached node IDs and re-resolve membership before calling Remote.
  4. Check the channel name again - a wrong channel can exist but contain a different consenter set, producing this error instead of 'channel doesn't exist'.

Example fix

// before
rc, err := comm.Remote("mychannel", 7) // node 7 removed in latest config
// after
comm.Configure("mychannel", currentChannelMembers) // refresh membership
if mapping.ByID(7) != nil { rc, err = comm.Remote("mychannel", 7) }
Defensive patterns

Strategy: validation

Validate before calling

func nodeInChannel(mgr *cluster.AuthCommMgr, channel string, id uint64) bool {
    mgr.Lock.RLock()
    defer mgr.Lock.RUnlock()
    mapping, ok := mgr.Chan2Members[channel]
    if !ok { return false }
    return mapping.ByID(id) != nil
}
if !nodeInChannel(mgr, "mychannel", nodeID) {
    return fmt.Errorf("node %d is not in mychannel's membership", nodeID)
}

Type guard

func nodeExists(mgr *cluster.AuthCommMgr, channel string, id uint64) bool {
    mgr.Lock.RLock()
    defer mgr.Lock.RUnlock()
    return mgr.Chan2Members[channel].ByID(id) != nil
}

Try / catch

rc, err := comm.Remote(channel, id)
if err != nil {
    if strings.Contains(err.Error(), "doesn't exist in channel") {
        // stale/invalid node ID: refresh membership from channel config and re-resolve
        comm.Configure(channel, fetchCurrentMembers(channel))
        rc, err = comm.Remote(channel, id)
    }
    return err
}

Prevention

When it happens

Trigger: Calling Remote(channel, id) where id is not in the RemoteNode set last passed to Configure(channel, members) for that channel - e.g. an ID that was removed in a config update, a never-existing consenter ID, or a stale ID cached by the caller (e.g. etcdraft tracking) after a membership change.

Common situations: Channel configuration reconfiguration removed/renumbered consenters while a caller still references the old node ID; hardcoded or mis-derived node IDs in custom chaincode/consenter code; TLS/endpoint changes that recreate stubs under different IDs; calling Remote against a channel whose consenter set excludes this cluster node.

Related errors


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