{"record":{"id":"f81d10b4d1e8b0c6","repo":"apache/cassandra","slug":"strict-consistency-requires-the-node-losing-the-ra","errorCode":null,"errorMessage":"Strict consistency requires the node losing the range to be UP but  is DOWN","messagePattern":"Strict consistency requires the node losing the range to be UP but  is DOWN","errorType":"exception","errorClass":"IllegalStateException","httpStatus":null,"severity":"error","filePath":"src/java/org/apache/cassandra/tcm/sequences/Move.java","lineNumber":559,"sourceCode":"                        // replica we need to make sure it remains a replica for the range after the move has finished:\n                        if (writeAdditions.get(source.endpoint()).byRange().get(destination.range()) == null)\n                        {\n                            if (strict)\n                                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));\n                            else\n                                return false;\n                        }\n                        return true;\n                    }\n                    else\n                    {\n                        transientSource = source;\n                    }\n                }\n                return true;\n            }\n            else if (strict)\n                throw new IllegalStateException(\"Strict consistency requires the node losing the range to be UP but \" + source + \" is DOWN\");\n            return false;\n        }\n\n        private void addToMovements(Replica destination, EndpointsByReplica.Builder movements)\n        {\n            if (fullSource != null)\n                movements.put(destination, fullSource);\n            if (transientSource != null)\n                movements.put(destination, transientSource);\n        }\n    }\n\n    private static int nextToIndex(Transformation.Kind next)\n    {\n        switch (next)\n        {\n            case START_MOVE:\n                return 0;","sourceCodeStart":541,"sourceCodeEnd":577,"githubUrl":"https://github.com/apache/cassandra/blob/88fd0f6a0eaed8943f05ac9e8f947882b8ddc8f1/src/java/org/apache/cassandra/tcm/sequences/Move.java#L541-L577","documentation":"Thrown by Move.addSource when strict consistency is requested for a consistent range movement but the node losing the range (the source) is currently DOWN per failure detection. A consistent move requires streaming hand-off from a live source; if it is dead the invariant cannot be maintained, so strict mode aborts while non-strict returns false and skips the source.","triggerScenarios":"Executing a strict-consistency range movement where a source replica of the range is marked DOWN by FailureDetector at planning time.","commonSituations":"Running nodetool move/assassinate cleanup workflows while a node is down; firewalled or partially partitioned clusters where failure detector falsely reports a live node down; attempting topology repair during an outage instead of waiting for recovery.","solutions":["Wait for / restore the source node to be UP (fix networking, restart the node) and retry the movement","Retry the operation with strict consistency disabled so the down source is skipped","Remove the down node from the cluster first (decommission/assassinate with appropriate procedures) and re-plan the movement","Check FailureDetector settings (phi threshold) if the node is actually alive but flapping"],"exampleFix":"// before\nMoveMultiRange.move(systemKeyspace, ranges, /* strict */ true); // fails while source is DOWN\n// after\nif (failureDetector.isAlive(source)) move(ranges, true); else move(ranges, false); // or wait for node to come up","handlingStrategy":"validation","validationCode":"// check all sources are alive before a strict move\nif (sources.stream().anyMatch(r -> !FailureDetector.instance.isAlive(r.endpoint())))\n    throw new IllegalStateException(\"abort: strict move requires all sources UP\");","typeGuard":null,"tryCatchPattern":"try { move(ranges, true); }\ncatch (IllegalStateException e) { if (e.getMessage().contains(\"is DOWN\")) scheduleRetryAfterNodeRecovery(); else throw e; }","preventionTips":["Check FailureDetector status of all involved nodes before starting strict movements","Only run topology operations during healthy-cluster windows","Investigate flapping failure detection (phi thresholds, networking) proactively"],"tags":["consistency","node-down","failure-detection","range-movement"],"backgroundTag":"node-down","analyzedSha":"88fd0f6a0eaed8943f05ac9e8f947882b8ddc8f1","analyzedAt":"2026-09-10T07:29:22.284Z","contentChangedAt":"2026-09-10T07:29:22.284Z","schemaVersion":2},"datasetVersion":"2026-09-17T15:17:12.973Z"}