apache/cassandra · error · IllegalStateException

Source %s for %s is not remaining as a replica after the mov

Error message

Source %s for %s is not remaining as a replica after the move, can't do a consistent range movement, retry with that disabled

What it means

Thrown by Move.addSource during consistent (strict) range move planning. A source replica of a range would no longer be a replica of that range after the move, which breaks the guarantee that a transient replica being removed can safely skip streaming. In strict mode this is fatal; in non-strict mode MoveMultiRange skips the source instead.

Source

Thrown at src/java/org/apache/cassandra/tcm/sequences/Move.java:545

        {
            if (fd.isAlive(source.endpoint()))
            {
                if (source.isFull())
                {
                    assert fullSource == null;
                    fullSource = source;
                }
                else
                {
                    assert transientSource == null;
                    if (!destination.isSelf() && !source.isSelf())
                    {
                        // a transient replica is being removed, now, to be able to safely skip streaming from this
                        // replica we need to make sure it remains a replica for the range after the move has finished:
                        if (writeAdditions.get(source.endpoint()).byRange().get(destination.range()) == null)
                        {
                            if (strict)
                                throw new IllegalStateException(String.format("Source %s for %s is not remaining as a replica after the move, can't do a consistent range movement, retry with that disabled", source, destination));
                            else
                                return false;
                        }
                        return true;
                    }
                    else
                    {
                        transientSource = source;
                    }
                }
                return true;
            }
            else if (strict)
                throw new IllegalStateException("Strict consistency requires the node losing the range to be UP but " + source + " is DOWN");
            return false;
        }

        private void addToMovements(Replica destination, EndpointsByReplica.Builder movements)

View on GitHub (pinned to 88fd0f6a0e)

Solutions

  1. Retry the operation with consistent range movement (strict consistency) disabled, as the message suggests
  2. Adjust the move plan / token assignment so the source remains a replica (full or transient) of the range after the move
  3. Verify transient replication configuration (replication factor and transient replica counts) allows every removed transient source to remain a replica elsewhere
  4. Perform the movement in smaller steps (per-range) so strict invariants hold at each step

Example fix

// before
MoveMultiRange.move(systemKeyspace, ranges, /* strict */ true);
// after
MoveMultiRange.move(systemKeyspace, ranges, /* strict */ false); // or fix placement so source remains a replica
Defensive patterns

Strategy: retry

Validate before calling

// before strict move: ensure each transient source remains a replica of its destination range
for (Replica source : sources)
    if (writeAdditions.get(source.endpoint()).byRange().get(destination.range()) == null)
        throw new PreconditionFailed(source + " will not remain a replica; use non-strict or fix placement");

Try / catch

try { move(ranges, true); }
catch (IllegalStateException e) { if (e.getMessage().contains("not remaining as a replica")) move(ranges, false); else throw e; }

Prevention

When it happens

Trigger: Running a consistent range movement (strict consistency enabled, e.g. nodetool move/rebuild with strict consistency or MoveMultiRange) where a transient source replica's writeAdditions entry has no matching range for the destination, i.e. the node is not staying a replica of destination.range() after the move.

Common situations: Operator-driven topology changes (moving tokens, decommission/bootstrap combinations) in multi-DC or transient-replication (EVERYWHERE/2-of-3 RF with transient replica counts) setups where the move leaves a range with too few full replicas; retry loops that previously used non-strict consistency now running strict.

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/0e1a1614e3c110a7. Report an issue: GitHub.