apache/cassandra · error · IllegalArgumentException
No group found for range of supplied replica
Error message
No group found for range of supplied replica %s (%s)
What it means
ReplicaGroups.Builder.withoutReplica removes a single replica from the placement group owning its range. It throws IllegalArgumentException when no group in the current replicaGroups map matches the replica's exact range, i.e. the caller asked to remove a replica from a range this builder does not track.
Solutions
- Re-read the current placements from ClusterMetadata and rebuild the Builder so the replica's range matches an existing group
- Confirm the operation is idempotent/retryable: if the range group is already gone the removal may have been applied; skip instead of throwing
- Ensure the replica object passed to withoutReplica comes from the same snapshot the Builder was created from (same epoch)
- Re-run the placement transformation from a fresh metadata read
Example fix
// before
builder.withoutReplica(epoch, replica);
// after
VersionedEndpoints.ForRange group = replicaGroups.get(replica.range());
if (group != null)
builder.withoutReplica(epoch, replica); Defensive patterns
Strategy: try-catch
Validate before calling
VersionedEndpoints.ForRange group = builderReplicaGroups.get(replica.range());
if (group == null || !group.get().contains(replica.endpoint()))
throw new IllegalArgumentException("builder lacks replica: " + replica); Try / catch
try { builder.withoutReplica(epoch, replica); } catch (IllegalArgumentException e) { /* skip: removal already applied or rebuild from fresh metadata */ } Prevention
- Always build the Builder and source replicas from the same metadata epoch/snapshot
- Make removal operations idempotent: treat missing ranges as already-applied
- Rebuild builders after any concurrent metadata transformation
When it happens
Trigger: Calling withoutReplica(epoch, replica) with a replica whose Range<Token> is not a key in the builder's replicaGroups map — typically when computing a placement delta (e.g. during decommission or range movement) using a replica taken from a different/stale placements snapshot.
Common situations: A decommission or move operation was partially applied so the range in the builder differs from the replica's range; passing replicas from a canonical placement into a builder seeded from the pending placement; concurrent metadata transformations changed the ranges between reads.
Understand the failure class
Background: 'Could not be found', 'does not exist', 'not found in database': the resource-not-found family when an ID, slug, key, or URI lookup comes back empty — this error's family across 20 libraries.
Related errors
- Proposed tokens must be superset of existing tokens
- New tokens exceed total bounds of current placement ranges
- Unknown key:
- Addresses differ: !=
- Bad CMS state:
AI-assisted analysis of apache/cassandra@88fd0f6a0e (2026-09-10).
Data as JSON: /api/errors/6ec5656b6dcb65ad.
Report an issue: GitHub.
Appendix: source
Thrown at src/java/org/apache/cassandra/tcm/ownership/ReplicaGroups.java:414
EndpointsForRange old = v.get();
return VersionedEndpoints.forRange(epoch,
old.newBuilder(old.size() + 1)
.addAll(old)
.add(replica, ReplicaCollection.Builder.Conflict.NONE)
.build());
});
; if (group == null)
replicaGroups.put(replica.range(), VersionedEndpoints.forRange(epoch, EndpointsForRange.of(replica)));
return this;
}
public Builder withoutReplica(Epoch epoch, Replica replica)
{
Range<Token> range = replica.range();
VersionedEndpoints.ForRange group = replicaGroups.get(range);
if (group == null)
throw new IllegalArgumentException(String.format("No group found for range of supplied replica %s (%s)",
replica, range));
EndpointsForRange without = group.get().without(Collections.singleton(replica.endpoint()));
if (without.isEmpty())
replicaGroups.remove(range);
else
replicaGroups.put(range, VersionedEndpoints.forRange(epoch, without));
return this;
}
public Builder withReplicaGroup(VersionedEndpoints.ForRange replicas)
{
VersionedEndpoints.ForRange group =
replicaGroups.computeIfPresent(replicas.range(),
(t, v) -> {
EndpointsForRange old = v.get();
return VersionedEndpoints.forRange(Epoch.max(v.lastModified(), replicas.lastModified()),
combine(old, replicas.get()));
});View on GitHub (pinned to 88fd0f6a0e)