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
- Check the node's state in the metadata first; if JOINED, no action is needed.
- Target a different node id if you intended another host.
- 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
- Inspect peerState before force join
- Make tool runs idempotent by checking state first
- Track which nodes were already force-joined
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
- Initial CMS node needs to be fully joined, not: %s
- Node ${nodeIpOrId} is in ${nodeState} state. Only a JOINED n
- Unable to stop gossip because the node is not in the normal
- Can't abort bootstrap for - it does not exist in cluster me
- Can't abort bootstrap for since it is not bootstrapping
AI-assisted analysis of apache/cassandra@88fd0f6a0e (2026-09-10).
Data as JSON: /api/errors/6d39a276443ce44a.
Report an issue: GitHub.