{"record":{"id":"ec7735310160a0b1","repo":"apache/cassandra","slug":"cannot-remove-self","errorCode":null,"errorMessage":"Cannot remove self","messagePattern":"Cannot remove self","errorType":"exception","errorClass":"UnsupportedOperationException","httpStatus":null,"severity":"error","filePath":"src/java/org/apache/cassandra/tcm/sequences/SingleNodeSequences.java","lineNumber":131,"sourceCode":"    }\n\n    static void abortDecommission(String nodeId)\n    {\n        abortHelper(nodeId, MultiStepOperation.Kind.LEAVE, DECOMMISSION_FAILED);\n    }\n\n    /**\n     * Entrypoint to begin node removal process\n     *\n     * @param toRemove id of the node to remove\n     * @param force if set to true, will remove the node even if this would mean there will be not enough nodes\n     *              to satisfy replication factor\n     */\n    static void removeNode(NodeId toRemove, boolean force)\n    {\n        ClusterMetadata metadata = ClusterMetadata.current();\n        if (toRemove.equals(metadata.myNodeId()))\n            throw new UnsupportedOperationException(\"Cannot remove self\");\n        InetAddressAndPort endpoint = metadata.directory.endpoint(toRemove);\n        if (endpoint == null)\n            throw new UnsupportedOperationException(\"Host ID not found.\");\n        if (Gossiper.instance.getLiveMembers().contains(endpoint))\n            throw new UnsupportedOperationException(\"Node \" + endpoint + \" is alive and owns this ID. Use decommission command to remove it from the ring\");\n\n        NodeState removeState = metadata.directory.peerState(toRemove);\n        if (removeState == null)\n            throw new UnsupportedOperationException(\"Node to be removed is not a member of the token ring\");\n        if (removeState == NodeState.LEAVING)\n            logger.warn(\"Node {} is already leaving or being removed, continuing removal anyway\", endpoint);\n\n        if (metadata.inProgressSequences.contains(toRemove))\n            throw new UnsupportedOperationException(\"Can not remove a node that has an in-progress sequence\");\n\n        ReconfigureCMS.maybeReconfigureCMS(metadata, endpoint);\n\n        logger.info(\"starting removenode with {} {}\", metadata.epoch, toRemove);","sourceCodeStart":113,"sourceCodeEnd":149,"githubUrl":"https://github.com/apache/cassandra/blob/88fd0f6a0eaed8943f05ac9e8f947882b8ddc8f1/src/java/org/apache/cassandra/tcm/sequences/SingleNodeSequences.java#L113-L149","documentation":"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.","triggerScenarios":"Invoking `nodetool removenode <hostId>` on a node passing its own host ID, which routes to SingleNodeSequences.removeNode with toRemove == metadata.myNodeId().","commonSituations":"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.","solutions":["Use `nodetool decommission` on the local node instead of removenode for self-removal.","Re-run removenode with the host ID of the actual dead node (`nodetool status` to list host IDs).","Verify the target ID with `nodetool statusbatch` / `nodetool status` before removal."],"exampleFix":"// before\nnodetool removenode $(nodetool info | awk '/Host ID/{print $4}')  # own ID\n// after\nString targetId = deadNodeHostId; // from nodetool status of another node\nif (!targetId.equals(localHostId)) nodetool.removenode(targetId);","handlingStrategy":"validation","validationCode":"String targetHostId = args[0];\nString localHostId = SystemKeyspace.getLocalHostId().toString();\nif (targetHostId.equals(localHostId))\n    throw new IllegalArgumentException(\"Refusing removenode of self; use `nodetool decommission` instead\");","typeGuard":null,"tryCatchPattern":"try {\n    storageService.removeNode(hostId);\n} catch (UnsupportedOperationException e) {\n    if (e.getMessage().equals(\"Cannot remove self\")) {\n        // correct operation for self-removal is decommission\n        storageService.decommission();\n    } else throw e;\n}","preventionTips":["Compare the requested host ID against the local host ID before calling removenode.","Use decommission for planned self-removal; removenode only for dead nodes.","Automate runbooks to fetch the target ID from `nodetool status`, not local info.","Document the decommission-vs-removenode distinction in ops guides."],"tags":["removenode","self-removal","topology","operator-error"],"backgroundTag":"invalid-argument-value","analyzedSha":"88fd0f6a0eaed8943f05ac9e8f947882b8ddc8f1","analyzedAt":"2026-09-10T07:29:22.284Z","contentChangedAt":"2026-09-10T07:29:22.284Z","schemaVersion":2},"datasetVersion":"2026-09-17T15:17:12.973Z"}