apache/cassandra · error · UnsupportedOperationException

Node to be removed is not a member of the token ring

Error message

Node to be removed is not a member of the token ring

What it means

removeNode requires the target to be a current ring member: metadata.directory.peerState(toRemove) must be non-null. When the NodeId resolves to an endpoint but has no node state in the directory, the node is not part of the token ring and removal is meaningless, so UnsupportedOperationException is thrown. (Note the same literal message also appears one line later at the null-check in the source; both guard against a non-member target.)

Solutions

  1. Confirm the target is a ring member via `nodetool status` / ClusterMetadata token map before removing.
  2. If the node never joined, no removenode is needed — clean up any stale state via the documented abort/cleanup tooling.
  3. If metadata is inconsistent, complete or abort the interrupted sequence that left the partial state, then retry.
  4. Use the correct, current host ID of the dead member you actually intend to remove.

Example fix

// before
nodetool removenode abortedBootstrapHostId
// after
NodeState st = ClusterMetadata.current().directory.peerState(toRemove);
if (st != null) nodetool.removenode(hostId);
else logger.warn("{} is not a ring member; skipping removal", toRemove);
Defensive patterns

Strategy: validation

Validate before calling

NodeId id = NodeId.fromString(hostId);
if (ClusterMetadata.current().directory.peerState(id) == null)
    throw new IllegalArgumentException("Node " + hostId + " is not a token-ring member; nothing to remove");

Try / catch

try {
    storageService.removeNode(hostId);
} catch (UnsupportedOperationException e) {
    if (e.getMessage().contains("not a member of the token ring")) {
        logger.info("Target {} is not a ring member; skipping removal", hostId);
    } else throw e;
}

Prevention

When it happens

Trigger: Calling `nodetool removenode` with a host ID whose directory entry lacks a NodeState — e.g. a half-registered node, an ID that maps to an endpoint that never joined, or an inconsistent metadata directory.

Common situations: Removal attempted for a node that was already partially removed or that failed mid-bootstrap and never became a member; TCM metadata contains a stale directory entry; operator uses an ID from an aborted bootstrap.

Understand the failure class

Background: 'Could not be found', 'does not exist', 'not found in database': the resource-not-found family when an ID, slug, key, or URI lookup comes back empty — this error's family across 20 libraries.

Related errors


AI-assisted analysis of apache/cassandra@88fd0f6a0e (2026-09-10). Data as JSON: /api/errors/5671c545510d93da. Report an issue: GitHub.

Appendix: source

Thrown at src/java/org/apache/cassandra/tcm/sequences/SingleNodeSequences.java:141

     * @param toRemove id of the node to remove
     * @param force if set to true, will remove the node even if this would mean there will be not enough nodes
     *              to satisfy replication factor
     */
    static void removeNode(NodeId toRemove, boolean force)
    {
        ClusterMetadata metadata = ClusterMetadata.current();
        if (toRemove.equals(metadata.myNodeId()))
            throw new UnsupportedOperationException("Cannot remove self");
        InetAddressAndPort endpoint = metadata.directory.endpoint(toRemove);
        if (endpoint == null)
            throw new UnsupportedOperationException("Host ID not found.");
        if (Gossiper.instance.getLiveMembers().contains(endpoint))
            throw new UnsupportedOperationException("Node " + endpoint + " is alive and owns this ID. Use decommission command to remove it from the ring");

        NodeState removeState = metadata.directory.peerState(toRemove);
        if (removeState == null)
            throw new UnsupportedOperationException("Node to be removed is not a member of the token ring");
        if (removeState == NodeState.LEAVING)
            logger.warn("Node {} is already leaving or being removed, continuing removal anyway", endpoint);

        if (metadata.inProgressSequences.contains(toRemove))
            throw new UnsupportedOperationException("Can not remove a node that has an in-progress sequence");

        ReconfigureCMS.maybeReconfigureCMS(metadata, endpoint);

        logger.info("starting removenode with {} {}", metadata.epoch, toRemove);
        Collection<Token> tokens  = metadata.tokenMap.tokens(toRemove);
        ClusterMetadataService.instance().commit(new PrepareLeave(toRemove,
                                                                  force,
                                                                  ClusterMetadataService.instance().placementProvider(),
                                                                  LeaveStreams.Kind.REMOVENODE));
        InProgressSequences.finishInProgressSequences(toRemove);
        Gossiper.instance.unsafeBroadcastLeftStatus(endpoint, tokens, metadata.directory.allJoinedEndpoints());
    }

    static void abortRemoveNode(String nodeId)

View on GitHub (pinned to 88fd0f6a0e)