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

  1. Verify the target address/host ID matches a live, UP, JOINED node (nodetool status)
  2. Remove the dead node properly (nodetool removenode) and bootstrap a new node instead of replacing
  3. Correct cassandra.replace_address to the actual down node's IP
  4. 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

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


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)