apache/cassandra · error · RuntimeException
Cannot replace node which is not currently joined
Error message
Cannot replace node %s which is not currently joined
What it means
In replace mode, the node named by cassandra.replace_address must exist in the cluster directory with peer state JOINED. If the address is unknown to the cluster or the peer is not in JOINED state, the replacement cannot take over its token range and a RuntimeException naming the address is thrown.
Solutions
- Verify the target address/host ID matches a live, UP, JOINED node (nodetool status)
- Remove the dead node properly (nodetool removenode) and bootstrap a new node instead of replacing
- Correct cassandra.replace_address to the actual down node's IP
- Wait until the target node shows JOINED state before attempting replace
Example fix
// before -Dcassandra.replace_address=10.0.0.99 # never joined // after -Dcassandra.replace_address=10.0.0.5 # verified down+JOINED in nodetool status
Defensive patterns
Strategy: validation
Validate before calling
// before replacing: confirm target is a known JOINED peer
Row r = exec("SELECT peer FROM system.peers"); assert targetInNodetoolStatusAndJoined(replaceAddress); Try / catch
try { startNode(); } catch (RuntimeException e) { if (e.getMessage().startsWith("Cannot replace node")) { /* verify address / ring state */ } } Prevention
- Confirm the dead node with nodetool status before setting replace_address
- Never replace nodes already removed from the ring
- Copy the exact IP from nodetool status, not from memory
When it happens
Trigger: initServer with cassandra.replace_address pointing at an IP never part of the cluster, a node that was decommissioned/removed, or a node still joining (not JOINED).
Common situations: Typo in cassandra.replace_address; replacing a node that was already removed via removenode/decommission; trying to replace during the target's own bootstrap; stale replace address after ring changes.
Understand the failure class
Background: "Must be a positive integer", "Invalid value", "Unsupported": the invalid-argument-value error family, when a library rejects the value you pass — this error's family across 35 libraries.
Related errors
- Cannot replace with a node that is already bootstrapped
- Replace method removed; use cassandra.replace_address…
- a hints file cannot be configured for both compression and…
- A maximum number of tokens per node is supported
- A node with address already exists, cancelling join. Use…
AI-assisted analysis of apache/cassandra@88fd0f6a0e (2026-09-10).
Data as JSON: /api/errors/a61662f990649bd4.
Report an issue: GitHub.
Appendix: source
Thrown at src/java/org/apache/cassandra/service/StorageService.java:818
}
else
{
throw new ConfigurationException("This node was decommissioned and will not rejoin the ring unless cassandra.override_decommission=true has been set, or all existing data is removed and the node is bootstrapped again");
}
}
if (DatabaseDescriptor.getReplaceTokens().size() > 0 || DatabaseDescriptor.getReplaceNode() != null)
throw new RuntimeException("Replace method removed; use cassandra.replace_address instead");
if (isReplacing())
{
if (SystemKeyspace.bootstrapComplete())
throw new RuntimeException("Cannot replace with a node that is already bootstrapped");
InetAddressAndPort replaceAddress = DatabaseDescriptor.getReplaceAddress();
Directory directory = ClusterMetadata.current().directory;
if (directory.peerId(replaceAddress) == null || directory.peerState(replaceAddress) != JOINED)
throw new RuntimeException(String.format("Cannot replace node %s which is not currently joined", replaceAddress));
BootstrapAndReplace.checkUnsafeReplace(shouldBootstrap());
}
if (isReplacingSameAddress())
{
BootstrapAndReplace.gossipStateToHibernate(ClusterMetadata.current(), ClusterMetadata.currentNullable().myNodeId());
Gossiper.instance.start(SystemKeyspace.incrementAndGetGeneration(), false);
}
else
{
Gossiper.instance.start(SystemKeyspace.incrementAndGetGeneration(),
ClusterMetadataService.state() != ClusterMetadataService.State.GOSSIP); // only populate local state if not running in gossip mode
}
Gossiper.instance.register(this);
Gossiper.instance.addLocalApplicationState(ApplicationState.NET_VERSION, valueFactory.networkVersion());
Gossiper.instance.addLocalApplicationState(ApplicationState.SSTABLE_VERSIONS,View on GitHub (pinned to 88fd0f6a0e)