apache/cassandra · error · IllegalStateException

Source for is not remaining as a replica after the move…

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.

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.

Appendix: 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)