apache/cassandra · warning · IllegalStateException

%s is already joining the CMS

Error message

%s is already joining the CMS

What it means

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.

Source

Thrown at src/java/org/apache/cassandra/tcm/CMSMembership.java:140

    public Epoch lastModified()
    {
        return lastModified;
    }

    public Set<NodeId> joiningMembers()
    {
        return joiningMembers;
    }

    public Set<NodeId> fullMembers()
    {
        return fullMembers;
    }

    public CMSMembership startJoining(NodeId id)
    {
        if (joiningMembers.contains(id))
            throw new IllegalStateException(id + " is already joining the CMS");
        if (fullMembers.contains(id))
            throw new IllegalStateException(id + " has already fully joined the CMS");

        return new CMSMembership(lastModified, fullMembers, joiningMembers.with(id));
    }

    public CMSMembership cancelJoining(NodeId id)
    {
        if (!joiningMembers.contains(id))
            throw new IllegalStateException(id + " is not currently joining the CMS");
        if (fullMembers.contains(id))
            throw new IllegalStateException(id + " has already fully joined the CMS");

        return new CMSMembership(lastModified, fullMembers, joiningMembers.without(id));
    }

    public CMSMembership finishJoining(NodeId id)
    {

View on GitHub (pinned to 88fd0f6a0e)

Solutions

  1. Check current CMS state (joining members) before retrying; if the node is already joining, wait for the join to finish instead of re-issuing
  2. Use idempotent join handling: catch IllegalStateException and treat 'already joining' as success in the orchestration
  3. Cancel the stale joining entry (cancelJoining) if the join attempt is truly dead, then start joining again
  4. De-duplicate concurrent join automation with a single coordination point/lock

Example fix

// before
Membership members = cms.join(nodeId); // may throw if already joining
// after
try { members = cms.join(nodeId); }
catch (IllegalStateException e) {
    if (e.getMessage().contains("already joining")) return; // idempotent
    throw e;
}
Defensive patterns

Strategy: try-catch

Validate before calling

if (membership.joiningMembers().contains(nodeId)) skipJoin(nodeId); // already in progress

Try / catch

try { membership = cms.startJoining(nodeId); }
catch (IllegalStateException e) {
    if (e.getMessage().endsWith("is already joining the CMS")) return; // idempotent no-op
    throw e;
}

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Understand the failure class

Background: "Invalid state transition" errors: "status must be X, actually Y", "already rejected/charging/uninstalled", "cannot ... while running" — what they mean when a library rejects your call — this error's family across 31 libraries.

Related errors


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