apache/cassandra · error · UnsupportedOperationException

Cannot remove self

Error message

Cannot remove self

What it means

removeNode refuses to remove a node by host ID when the requested NodeId equals the caller's own node ID, throwing UnsupportedOperationException. Removing yourself from the ring must go through the decommission path instead, which drains tokens gracefully.

Solutions

  1. Use `nodetool decommission` on the local node instead of removenode for self-removal.
  2. Re-run removenode with the host ID of the actual dead node (`nodetool status` to list host IDs).
  3. Verify the target ID with `nodetool statusbatch` / `nodetool status` before removal.

Example fix

// before
nodetool removenode $(nodetool info | awk '/Host ID/{print $4}')  # own ID
// after
String targetId = deadNodeHostId; // from nodetool status of another node
if (!targetId.equals(localHostId)) nodetool.removenode(targetId);
Defensive patterns

Strategy: validation

Validate before calling

String targetHostId = args[0];
String localHostId = SystemKeyspace.getLocalHostId().toString();
if (targetHostId.equals(localHostId))
    throw new IllegalArgumentException("Refusing removenode of self; use `nodetool decommission` instead");

Try / catch

try {
    storageService.removeNode(hostId);
} catch (UnsupportedOperationException e) {
    if (e.getMessage().equals("Cannot remove self")) {
        // correct operation for self-removal is decommission
        storageService.decommission();
    } else throw e;
}

Prevention

When it happens

Trigger: Invoking `nodetool removenode <hostId>` on a node passing its own host ID, which routes to SingleNodeSequences.removeNode with toRemove == metadata.myNodeId().

Common situations: Operator intends to decommission the local node but types its own host ID into removenode; copy/paste error in a runbook; scripted removal grabs the wrong node ID from a listing.

Understand the failure class

Background: "Must be a positive integer", "Invalid value", "Unsupported": the invalid-argument-value error family, when a library rejects the value you pass — this error's family across 35 libraries.

Related errors


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

Appendix: source

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

    }

    static void abortDecommission(String nodeId)
    {
        abortHelper(nodeId, MultiStepOperation.Kind.LEAVE, DECOMMISSION_FAILED);
    }

    /**
     * Entrypoint to begin node removal process
     *
     * @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);

View on GitHub (pinned to 88fd0f6a0e)