apache/cassandra · error · IllegalArgumentException

Node ${nodeIpOrId} is already in JOINED state.

Error message

Node ${nodeIpOrId} is already in JOINED state.

What it means

Thrown by the ForceJoin transformation in CMSOfflineTool when metadata.directory.peerState(nodeId) reports NodeState.JOINED for the target node. A force join only makes sense for nodes not fully joined, so re-joining an already-joined node is rejected as an invalid state transition.

Source

Thrown at src/java/org/apache/cassandra/tools/CMSOfflineTool.java:653

        @CommandLine.ArgGroup(exclusive = true, multiplicity = "1")
        NodeIdentifierOption nodeIdentifierOption;
        @Option(names = { "-o", "--output-file" },
        description = "Output file path for storing the updated Cluster Metadata.")
        private String outputFilePath;

        @Override
        protected void execute(Output output) throws IOException
        {
            ClusterMetadata metadata = parseClusterMetadata();
            NodeId nodeId = nodeIdentifierOption.getNodeId(metadata);
            Set<Token> tokenSet = new HashSet<>(tokens.size());
            Token.TokenFactory tokenFactory = metadata.partitioner.getTokenFactory();
            tokens.forEach(t -> tokenSet.add(tokenFactory.fromString(t)));

            NodeState nodeState = metadata.directory.peerState(nodeId);
            if (nodeState == NodeState.JOINED)
            {
                throw new IllegalArgumentException("Node " + nodeIdentifierOption.getNodeIpOrId() +
                                                   " is already in JOINED state.");
            }

            ClusterMetadata updatedMetadata;
            if (metadata.inProgressSequences.get(nodeId) != null)
            {
                MultiStepOperation<?> multiStepOperation = metadata.inProgressSequences.get(nodeId);
                if (multiStepOperation.kind() != MultiStepOperation.Kind.JOIN)
                {
                    throw new IllegalArgumentException("Another sequence of kind " + multiStepOperation.kind() +
                                                       " is in progress for node " + nodeIdentifierOption.getNodeIpOrId() +
                                                       ". Cannot proceed with force join.");
                }
                BootstrapAndJoin bootstrapAndJoin = (BootstrapAndJoin) multiStepOperation;
                Set<Token> sequenceTokens = bootstrapAndJoin.finishJoin.tokens;
                if (tokens.isEmpty()
                    || (tokenSet.size() == sequenceTokens.size() && sequenceTokens.containsAll(tokenSet)))
                {

View on GitHub (pinned to 88fd0f6a0e)

Solutions

  1. Check the node's state in the metadata first; if JOINED, no action is needed.
  2. Target a different node id if you intended another host.
  3. Use the appropriate offline operation for an already-joined node (e.g. restart, or cleanup) instead of force join.

Example fix

// before
// forceJoin(metadata, idOfJoinedNode, tokens)
// after
// if (metadata.directory.peerState(nodeId) != NodeState.JOINED) forceJoin(...);
Defensive patterns

Strategy: validation

Validate before calling

if (metadata.directory.peerState(nodeId) == NodeState.JOINED)
    return; // nothing to force-join

Try / catch

try { forceJoin(metadata, nodeId, tokens); } catch (IllegalArgumentException e) { logger.info("Skipping: " + e.getMessage()); }

Prevention

When it happens

Trigger: Running the offline force-join subcommand against a node whose peer state in the cluster metadata is already JOINED.

Common situations: Re-running the tool after a previous force join succeeded; assuming a node is un-joined because it is down; operating on the wrong node id.

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/6d39a276443ce44a. Report an issue: GitHub.