{"record":{"id":"f6ef6bbf065a59fa","repo":"apache/cassandra","slug":"s-is-already-joining-the-cms","errorCode":null,"errorMessage":"%s is already joining the CMS","messagePattern":"(.+?) is already joining the CMS","errorType":"exception","errorClass":"IllegalStateException","httpStatus":null,"severity":"warning","filePath":"src/java/org/apache/cassandra/tcm/CMSMembership.java","lineNumber":140,"sourceCode":"    public Epoch lastModified()\n    {\n        return lastModified;\n    }\n\n    public Set<NodeId> joiningMembers()\n    {\n        return joiningMembers;\n    }\n\n    public Set<NodeId> fullMembers()\n    {\n        return fullMembers;\n    }\n\n    public CMSMembership startJoining(NodeId id)\n    {\n        if (joiningMembers.contains(id))\n            throw new IllegalStateException(id + \" is already joining the CMS\");\n        if (fullMembers.contains(id))\n            throw new IllegalStateException(id + \" has already fully joined the CMS\");\n\n        return new CMSMembership(lastModified, fullMembers, joiningMembers.with(id));\n    }\n\n    public CMSMembership cancelJoining(NodeId id)\n    {\n        if (!joiningMembers.contains(id))\n            throw new IllegalStateException(id + \" is not currently joining the CMS\");\n        if (fullMembers.contains(id))\n            throw new IllegalStateException(id + \" has already fully joined the CMS\");\n\n        return new CMSMembership(lastModified, fullMembers, joiningMembers.without(id));\n    }\n\n    public CMSMembership finishJoining(NodeId id)\n    {","sourceCodeStart":122,"sourceCodeEnd":158,"githubUrl":"https://github.com/apache/cassandra/blob/88fd0f6a0eaed8943f05ac9e8f947882b8ddc8f1/src/java/org/apache/cassandra/tcm/CMSMembership.java#L122-L158","documentation":"CMSMembership is an immutable TCM transformation result representing CMS membership state. startJoining(id) throws IllegalStateException when the requested node id is already in the joiningMembers set — the transformation to mark it as joining would create a duplicate, so it is rejected.","triggerScenarios":"A CMS join/StartJoining transformation is applied (via a CMS processor commit) for a NodeId that is already tracked as joining; typically a retried join request or two concurrent join attempts for the same node.","commonSituations":"Retrying a failed/timeout join command without checking its outcome; operator running the CMS-join procedure twice; automation races issuing join for the same node from two places.","solutions":["Check current CMS state (joining members) before retrying; if the node is already joining, wait for the join to finish instead of re-issuing","Use idempotent join handling: catch IllegalStateException and treat 'already joining' as success in the orchestration","Cancel the stale joining entry (cancelJoining) if the join attempt is truly dead, then start joining again","De-duplicate concurrent join automation with a single coordination point/lock"],"exampleFix":"// before\nMembership members = cms.join(nodeId); // may throw if already joining\n// after\ntry { members = cms.join(nodeId); }\ncatch (IllegalStateException e) {\n    if (e.getMessage().contains(\"already joining\")) return; // idempotent\n    throw e;\n}","handlingStrategy":"try-catch","validationCode":"if (membership.joiningMembers().contains(nodeId)) skipJoin(nodeId); // already in progress","typeGuard":null,"tryCatchPattern":"try { membership = cms.startJoining(nodeId); }\ncatch (IllegalStateException e) {\n    if (e.getMessage().endsWith(\"is already joining the CMS\")) return; // idempotent no-op\n    throw e;\n}","preventionTips":["Check joiningMembers before issuing a join","Make join automation idempotent","Serialize join requests for a node through a single coordinator"],"tags":["tcm","cms","join"],"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"}