{"record":{"id":"f3c03b7d15741989","repo":"apache/cassandra","slug":"couldn-t-find-any-matching-sufficient-replica-out-f3c03b","errorCode":null,"errorMessage":"Couldn't find any matching sufficient replica out of:  -> ","messagePattern":"Couldn't find any matching sufficient replica out of:  -> ","errorType":"exception","errorClass":"IllegalStateException","httpStatus":null,"severity":"error","filePath":"src/java/org/apache/cassandra/tcm/sequences/Move.java","lineNumber":479,"sourceCode":"    {\n        MovementMap.Builder allMovements = MovementMap.builder();\n        toStart.forEach((params, delta) -> {\n            RangesByEndpoint targets = delta.writes.additions(endpointLookup);\n            ReplicaGroups oldOwners = placements.get(params).reads;\n            EndpointsByReplica.Builder movements = new EndpointsByReplica.Builder();\n            Iterable<Replica> replicaRemovals = midDeltas.get(params).reads.removals(endpointLookup).flattenValues();\n            RangesByEndpoint writeAdditions = toSplitRanges.get(params).writes.additions(endpointLookup);\n            targets.flattenValues().forEach(destination -> {\n                SourceHolder sources = new SourceHolder(fd, destination, writeAdditions, strictConsistency);\n                AtomicBoolean needsRelaxedSources = new AtomicBoolean();\n                // first, try to find strict sources for the ranges we need to stream - these are the ranges that\n                // instances are losing.\n                replicaRemovals.forEach(strictSource -> {\n                    if (strictSource.range().equals(destination.range()) && !strictSource.endpoint().equals(destination.endpoint()))\n                        if (!sources.addSource(strictSource))\n                        {\n                            if (!strictConsistency)\n                                throw new IllegalStateException(\"Couldn't find any matching sufficient replica out of: \" + strictSource + \" -> \" + destination);\n                            needsRelaxedSources.set(true);\n                        }\n                });\n                // if we are not running with strict consistency, try to find other sources for streaming\n                if (needsRelaxedSources.get())\n                {\n                    for (Replica source : DatabaseDescriptor.getNodeProximity()\n                                                            .sortedByProximity(FBUtilities.getBroadcastAddressAndPort(),\n                                                                               oldOwners.forRange(destination.range()).get()))\n                    {\n                        if (fd.isAlive(source.endpoint()) && !source.endpoint().equals(destination.endpoint()))\n                        {\n                            if ((sources.fullSource == null && source.isFull()) ||\n                                (sources.transientSource == null && source.isTransient()))\n                                sources.addSource(source);\n                        }\n                    }\n                }","sourceCodeStart":461,"sourceCodeEnd":497,"githubUrl":"https://github.com/apache/cassandra/blob/88fd0f6a0eaed8943f05ac9e8f947882b8ddc8f1/src/java/org/apache/cassandra/tcm/sequences/Move.java#L461-L497","documentation":"While building the movement map for a move, for each range being lost by a strict source, the code needs another sufficient replica within the same range as a streaming source. If no matching replica can be added and consistency is strict, it throws IllegalStateException; otherwise it flags needsRelaxedSources to fall back to relaxed source selection. It protects against streaming data from replicas that don't actually hold the range.","triggerScenarios":"movementMap finds a strictConsistency source candidate whose range matches the destination range but whose endpoint equals the destination, or no candidate passes sources.addSource(); thrown when strict consistency is required and no valid replica exists for the range.","commonSituations":"Insufficient replication factor for the moved range (RF too low for the datacenter); other replicas of the range are down so no live source exists; moving a node in a single-replica range scenario.","solutions":["Run a repair (nodetool repair) to ensure other replicas actually hold the range, then retry the move","Increase the replication factor or bring up additional replicas for the affected ranges","Run the move without strict consistency only after confirming data availability (relaxed mode risks stale data)","Bring previously-down replicas back online so a valid source exists"],"exampleFix":"// before\n// move attempted with RF=1 and other replica down\n// after\n// nodetool repair <keyspace>; nodetool status to confirm live replicas; then nodetool move","handlingStrategy":"validation","validationCode":"for (Range<Token> range : movingRanges)\n    if (liveReplicasHolding(range).size() < 2)\n        throw new IllegalStateException(\"No sufficient replica available for \" + range);","typeGuard":null,"tryCatchPattern":"try { move.execute(); } catch (IllegalStateException e) { logger.error(\"No valid streaming source: {}\", e.getMessage()); scheduleRepairAndRetry(); }","preventionTips":["Keep RF high enough that each range has multiple live replicas","Run repairs regularly so replicas actually hold their ranges","Verify nodetool status shows enough live nodes per DC before topology changes"],"tags":["move","replication","streaming-sources","consistency"],"backgroundTag":"empty-result-set","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"}