apache/cassandra · error · IllegalArgumentException

New tokens exceed total bounds of current placement ranges

Error message

New tokens exceed total bounds of current placement ranges 

What it means

splitRangesForPlacement splits existing placement ranges so new tokens become range boundaries. Before splitting it validates that the proposed tokens fit within the total bounds [min, max] of the current placement ranges; IllegalArgumentException is thrown when any token falls outside those bounds, since no existing range could be split to cover it.

Solutions

  1. Verify every proposed token lies within the current placement range bounds before calling the API (tokens.get(0) >= min and tokens.last <= max)
  2. Re-assign tokens using the same partitioner/random token allocator that produced the existing ring
  3. If the ring is intentionally being extended, first allocate initial ranges covering the new tokens (initialize as a new datacenter/cluster operation) rather than splitting
  4. Correct the token configuration (initial_token / bootstrap token list) and retry the operation

Example fix

// before
cluster.bootstrap(toBootstrap.stream().map(TokenUtils::getToken).collect(Collectors.toSet()));
// after
SortedSet<Token> sorted = ...;
Token min = placements.first().range().left, max = placements.last().range().right;
if (sorted.first().compareTo(min) < 0 || sorted.last().compareTo(max) > 0)
    throw new IllegalArgumentException("token outside current placement bounds");
cluster.bootstrap(sorted);
Defensive patterns

Strategy: validation

Validate before calling

Token min = eprs.get(0).range().left, max = eprs.get(eprs.size()-1).range().right;
if (tokens.first().compareTo(min) < 0 || tokens.last().compareTo(max) > 0)
    throw new IllegalArgumentException("proposed tokens outside current placement bounds");

Try / catch

try { splitRangesForPlacement(tokens, eprs, epoch); } catch (IllegalArgumentException e) { /* regenerate tokens with the cluster's partitioner */ }

Prevention

When it happens

Trigger: Calling the TCM bootstrap/replace/move placement APIs (which route through splitRangesForPlacement) with a token set whose minimum is below the left boundary or whose maximum is above the right boundary of the current canonical placements.

Common situations: Assigning a new token outside the currently allocated token space (e.g. wrong partitioner ring bounds); attempting a multi-token bootstrap where one token was accidentally mistyped; replaying placement deltas against ranges that were never allocated (empty or partial ring).

Understand the failure class

Background: "value must be between 0 and 1" / "out of range" / "must not be negative" errors: fixing range-validation failures across open-source libraries — this error's family across 42 libraries.

Related errors


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

Appendix: source

Thrown at src/java/org/apache/cassandra/tcm/ownership/ReplicaGroups.java:317

    {
        return new Builder(expectedSize);
    }

    @VisibleForTesting
    public static ReplicaGroups splitRangesForPlacement(List<Token> tokens, ReplicaGroups placement)
    {
        if (placement.ranges.isEmpty())
            return placement;

        Builder newPlacement = ReplicaGroups.builder();
        List<VersionedEndpoints.ForRange> eprs = new ArrayList<>(placement.endpoints);
        eprs.sort(Comparator.comparing(a -> a.range().left));
        Token min = eprs.get(0).range().left;
        Token max = eprs.get(eprs.size() - 1).range().right;

        // if any token is < the start or > the end of the ranges covered, error
        if (tokens.get(0).compareTo(min) < 0 || (!max.equals(min) && tokens.get(tokens.size()-1).compareTo(max) > 0))
            throw new IllegalArgumentException("New tokens exceed total bounds of current placement ranges " + tokens + " " + eprs);
        Iterator<VersionedEndpoints.ForRange> iter = eprs.iterator();
        VersionedEndpoints.ForRange current = iter.next();
        for (Token token : tokens)
        {
            // handle special case where one of the tokens is the min value
            if (token.equals(min))
                continue;

            assert current != null : tokens + " " + eprs;
            Range<Token> r = current.get().range();
            int cmp = token.compareTo(r.right);
            if (cmp == 0)
            {
                newPlacement.withReplicaGroup(current);
                if (iter.hasNext())
                    current = iter.next();
                else
                    current = null;

View on GitHub (pinned to 88fd0f6a0e)