apache/cassandra · error · IllegalStateException

Multiple strict sources found for %s, sources: %s

Error message

Multiple strict sources found for %s, sources: %s

What it means

With strict consistency enabled, a range must be fetched from at most one full and one transient source to preserve data correctness guarantees during range movements. If the computed address list contains more than one full or more than one transient strict source, RangeStreamer throws IllegalStateException because it cannot choose safely among multiple strict sources.

Source

Thrown at src/java/org/apache/cassandra/dht/RangeStreamer.java:562

                 sources = sources.size() > 0 ? sources.subList(0, 1) : sources;
             }

             // storing range and preferred endpoint set
             rangesToFetchWithPreferredEndpoints.putAll(toFetch, sources, Conflict.NONE);
             logger.debug("Endpoints to fetch for {} are {}", toFetch, sources);

             EndpointsForRange addressList = rangesToFetchWithPreferredEndpoints.getIfPresent(toFetch);
             if (addressList == null)
                 throw new IllegalStateException("Failed to find endpoints to fetch " + toFetch);

             /*
              * When we move forwards (shrink our bucket) we are the one losing a range and no one else loses
              * from that action (we also don't gain). When we move backwards there are two people losing a range. One is a full replica
              * and the other is a transient replica. So we must need fetch from two places in that case for the full range we gain.
              * For a transient range we only need to fetch from one.
              */
             if (useStrictConsistency && addressList.size() > 1 && (addressList.filter(Replica::isFull).size() > 1 || addressList.filter(Replica::isTransient).size() > 1))
                 throw new IllegalStateException(String.format("Multiple strict sources found for %s, sources: %s", toFetch, addressList));

             //We must have enough stuff to fetch from
             if (!any(addressList, isSufficient))
             {
                 if (strat.getReplicationFactor().allReplicas == 1)
                 {
                     if (useStrictConsistency)
                     {
                         logger.warn("A node required to move the data consistently is down");
                         throw new IllegalStateException("Unable to find sufficient sources for streaming range " + toFetch + " in keyspace " + keyspace + " with RF=1. " +
                                                         "Ensure this keyspace contains replicas in the source datacenter.");
                     }
                     else
                         logger.warn("Unable to find sufficient sources for streaming range {} in keyspace {} with RF=1. " +
                                     "Keyspace might be missing data.", toFetch, keyspace);
                 }
                 else
                 {

View on GitHub (pinned to 88fd0f6a0e)

Solutions

  1. Verify token metadata consistency with `nodetool describering` / `nodetool status`; resolve overlapping ranges (usually via repair + restart)
  2. Allow any in-flight move/decommission/bootstrap operations to complete before streaming, then retry
  3. If caused by a bug in range-movement bookkeeping, upgrade to a version with fixes to RangeStreamer strict handling
  4. Avoid mixing move and bootstrap operations concurrently on overlapping ranges
Defensive patterns

Strategy: try-catch

Validate before calling

// check for overlapping ownership before streaming
if (tokenMetadata.getPendingRanges(keyspace).size() > 0)
    throw new IllegalStateException("Pending ranges exist; complete in-flight operations before strict streaming");

Try / catch

try { streamer.fetch(); } catch (IllegalStateException e) { if (e.getMessage().startsWith("Multiple strict sources found")) { waitForOperationsToSettle(); retry(); } else throw e; }

Prevention

When it happens

Trigger: calculateRangesToFetchWithPreferredEndpoints() with useStrictConsistency=true and an addressList where filter(Replica::isFull).size() > 1 or filter(Replica::isTransient).size() > 1 for a single range.

Common situations: Complex range movement scenarios (move/decommission in flight) producing overlapping ownership; token metadata inconsistencies after failed operations; incorrect strict-consistency setting combined with unusual topology changes.

Understand the failure class

Background: "This is a bug, please report it": internal invariant violations, unreachable panics, and SNH errors explained — this error's family across 47 libraries.

Related errors


AI-assisted analysis of apache/cassandra@88fd0f6a0e (2026-09-10). Data as JSON: /api/errors/080d887f4eb26526. Report an issue: GitHub.