apache/cassandra · error · IllegalArgumentException
Proposed tokens must be superset of existing tokens
Error message
Proposed tokens must be superset of existing tokens
What it means
UniformRangePlacement.splitRanges merges proposed tokens with current canonical placements. When the proposed token set does not already contain every existing token, it checks whether the proposal is a superset of the existing tokens; IllegalArgumentException is thrown when it is not, because shrinking the token set would orphan existing ranges that cannot be re-derived by splitting.
Solutions
- Include all existing tokens in the proposed token set (proposedTokens.containsAll(currentTokens)) before invoking placement computation
- Reconstruct the node's token list from cluster metadata instead of local config, and pass the union of current + new tokens
- If tokens were intentionally removed, use the proper token-removal/decommission flow instead of the range-splitting path
- Fix local initial_token/auto-bootstrap config so the proposal matches the metadata's current tokens
Example fix
// before SortedSet<Token> proposed = newTokens; UniformRanges after = placements.splitRanges(proposed, currentPlacements); // after SortedSet<Token> proposed = new TreeSet<>(currentTokens); proposed.addAll(newTokens); UniformRanges after = placements.splitRanges(proposed, currentPlacements);
Defensive patterns
Strategy: validation
Validate before calling
if (!proposed.containsAll(currentTokens))
throw new IllegalArgumentException("proposed tokens must include all existing tokens"); Try / catch
try { placements.splitRanges(proposed, current); } catch (IllegalArgumentException e) { /* union proposed with currentTokens and retry */ } Prevention
- Always compute proposed token sets as union of existing + new tokens
- Derive current tokens from ClusterMetadata, not local node config
- Use dedicated flows (decommission/removenode) to shrink token sets
When it happens
Trigger: Calling placement APIs (start/finalPlacement during bootstrap, replace, or multi-token operations) with a proposed token set that is missing at least one currently assigned token — e.g. computing placements for a subset of tokens that excludes an already-owned token.
Common situations: Bootstrapping only a few nodes' tokens while the placement computation passes the whole current token set incorrectly; a node restart with a changed token list dropped one of its original tokens; misconfigured initial_token after a previous bootstrap.
Understand the failure class
Background: "Must be a positive integer", "Invalid value", "Unsupported": the invalid-argument-value error family, when a library rejects the value you pass — this error's family across 35 libraries.
Related errors
- New tokens exceed total bounds of current placement ranges
- No group found for range of supplied replica
- Unknown key:
- When reinitializing with cluster metadata, we must be in…
- Addresses differ: !=
AI-assisted analysis of apache/cassandra@88fd0f6a0e (2026-09-10).
Data as JSON: /api/errors/52fa2f5fe9999d4e.
Report an issue: GitHub.
Appendix: source
Thrown at src/java/org/apache/cassandra/tcm/ownership/UniformRangePlacement.java:285
if (metadata.directory.commonSerializationVersion.isBefore(Version.V10))
return plan.withEndpointDeltas(directory);
return plan;
}
public DataPlacements splitRanges(TokenMap current,
TokenMap proposed,
DataPlacements currentPlacements)
{
ImmutableList<Token> currentTokens = current.tokens();
ImmutableList<Token> proposedTokens = proposed.tokens();
if (currentTokens.isEmpty() || currentTokens.equals(proposedTokens))
{
return currentPlacements;
}
else
{
if (!proposedTokens.containsAll(currentTokens))
throw new IllegalArgumentException("Proposed tokens must be superset of existing tokens");
// we need to split some existing ranges, so apply the new set of tokens to the current canonical
// placements to get a set of placements with the proposed ranges but the current replicas
return splitRangesForAllPlacements(proposedTokens, currentPlacements);
}
}
@VisibleForTesting
DataPlacements splitRangesForAllPlacements(List<Token> proposedTokens, DataPlacements current)
{
DataPlacements.Builder builder = DataPlacements.builder(current.size());
current.asMap().forEach((params, placement) -> {
// Don't split ranges for local-only placements
if (params.isLocal() || params.isMeta())
builder.with(params, placement);
else
builder.with(params, placement.splitRangesForPlacement(proposedTokens));
});
return builder.build();View on GitHub (pinned to 88fd0f6a0e)