{"record":{"id":"1a0ff63bc059e3a5","repo":"apache/cassandra","slug":"can-not-remove-a-node-that-has-an-in-progress-sequ","errorCode":null,"errorMessage":"Can not remove a node that has an in-progress sequence","messagePattern":"Can not remove a node that has an in-progress sequence","errorType":"exception","errorClass":"java.lang.UnsupportedOperationException","httpStatus":null,"severity":"error","filePath":"src/java/org/apache/cassandra/tcm/sequences/SingleNodeSequences.java","lineNumber":145,"sourceCode":"    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);\n        Collection<Token> tokens  = metadata.tokenMap.tokens(toRemove);\n        ClusterMetadataService.instance().commit(new PrepareLeave(toRemove,\n                                                                  force,\n                                                                  ClusterMetadataService.instance().placementProvider(),\n                                                                  LeaveStreams.Kind.REMOVENODE));\n        InProgressSequences.finishInProgressSequences(toRemove);\n        Gossiper.instance.unsafeBroadcastLeftStatus(endpoint, tokens, metadata.directory.allJoinedEndpoints());\n    }\n\n    static void abortRemoveNode(String nodeId)\n    {\n        abortHelper(nodeId, MultiStepOperation.Kind.REMOVE, null);\n    }\n","sourceCodeStart":127,"sourceCodeEnd":163,"githubUrl":"https://github.com/apache/cassandra/blob/88fd0f6a0eaed8943f05ac9e8f947882b8ddc8f1/src/java/org/apache/cassandra/tcm/sequences/SingleNodeSequences.java#L127-L163","documentation":"SingleNodeSequences.removeNode refuses to remove a node while ClusterMetadata still records an in-progress multi-step sequence (leave/move/decommission/remove) for that node. Thrown as UnsupportedOperationException because concurrent topology operations on the same node are not supported; the previous operation must be finished or aborted first.","triggerScenarios":"Calling `nodetool removenode <hostId>` (which invokes removeNode) while the target node still appears in metadata.inProgressSequences - e.g. a prior decommission, move, or removenode was started but not completed or aborted.","commonSituations":"Operators retry a failed removenode/decommission without first aborting it; a node crashed mid-decommission leaving the sequence registered; scripted topology changes overlap (move then immediate remove).","solutions":["Wait for or complete the in-progress sequence (resume/finish the decommission/move) before removing the node.","Abort the in-progress sequence first, e.g. `nodetool abortdecommission`, `nodetool abortremovenode`, or the matching abort command, then retry removenode.","Inspect `nodetool inprogresssequences` / ClusterMetadata.inProgressSequences to confirm the node and sequence kind, then choose resume vs abort.","As a last resort after cluster recovery, verify the sequence is truly stale and clear it via the supported recovery path before re-running removenode."],"exampleFix":"// before\nnodetool removenode <hostId>            // throws: Can not remove a node that has an in-progress sequence\n// after\nnodetool abortremovenode <hostId>       # or abortdecommission / resume the operation\nnodetool removenode <hostId>","handlingStrategy":"validation","validationCode":"ClusterMetadata md = ClusterMetadata.current();\nif (md.inProgressSequences.contains(toRemove))\n    throw new IllegalStateException(\"defer removenode: sequence in progress for \" + toRemove);","typeGuard":null,"tryCatchPattern":"try { SingleNodeSequences.removeNode(id, force); }\ncatch (UnsupportedOperationException e) { /* sequence in progress - resume/abort first, then retry */ }","preventionTips":["Check inProgressSequences before any removenode call","Serialize topology operations; never run move/decommission/remove concurrently","Always abort failed operations before re-issuing them"],"tags":["cassandra","cluster-metadata","topology","node-removal"],"backgroundTag":"invalid-state-transition","analyzedSha":"88fd0f6a0eaed8943f05ac9e8f947882b8ddc8f1","analyzedAt":"2026-09-10T07:29:22.284Z","contentChangedAt":"2026-09-10T07:29:22.284Z","schemaVersion":2},"datasetVersion":"2026-09-14T16:17:12.679Z"}