apache/cassandra · error · IllegalArgumentException
Ranges supplied to SSTableCursorReader must be…
Error message
Ranges supplied to SSTableCursorReader must be non-overlapping and in ascending order.
What it means
SSTableCursorReader reads a sequence of byte-range segments from an sstable and requires them strictly ordered. advanceSegment() detects that the next range starts before the current segment's end (overlap or descending order) and throws IllegalArgumentException.
Solutions
- Sort the ranges by lowerPosition before constructing the reader.
- Coalesce overlapping/adjacent ranges before passing them in.
- Pre-validate the segments array with a loop and fail fast with a clearer error.
- Fix the producer of the bounds so it emits non-overlapping ascending ranges.
Example fix
// before
SSTableCursorReader reader = new SSTableCursorReader(..., segments);
// after
Arrays.sort(segments, Comparator.comparingLong(PartitionPositionBounds::lowerPosition));
for (int i = 1; i < segments.length; i++)
if (segments[i-1].upperPosition > segments[i].lowerPosition)
throw new IllegalArgumentException("overlapping range at index " + i);
SSTableCursorReader reader = new SSTableCursorReader(..., segments); Defensive patterns
Strategy: validation
Validate before calling
for (int i = 1; i < segments.length; i++) {
if (segments[i].lowerPosition < segments[i-1].upperPosition)
throw new IllegalArgumentException("overlapping/out-of-order range at " + i);
if (segments[i].upperPosition < segments[i].lowerPosition)
throw new IllegalArgumentException("inverted range at " + i);
} Try / catch
try {
reader = new SSTableCursorReader(..., segments);
} catch (IllegalArgumentException e) {
// re-sort/coalesce ranges and retry once
} Prevention
- Always sort ranges by lowerPosition before constructing the reader.
- Coalesce overlapping ranges into merged bounds.
- Unit-test range producers for empty, adjacent, and overlapping inputs.
When it happens
Trigger: Constructing SSTableCursorReader with a segments array where an earlier range's upperPosition exceeds a later range's lowerPosition.
Common situations: Custom tooling building PartitionPositionBounds lists without sorting; off-by-one errors when slicing an sstable for parallel scanning; merging ranges from multiple sources unsorted.
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
- A range supplied to SSTableCursorReader ends before it…
- Invalid negative chunk index
- The requested position exceeds the index length
- The requested position exceeds the index length
- 3
AI-assisted analysis of apache/cassandra@88fd0f6a0e (2026-09-10).
Data as JSON: /api/errors/856e62baf4533529.
Report an issue: GitHub.
Appendix: source
Thrown at src/java/org/apache/cassandra/io/sstable/SSTableCursorReader.java:679
*/
private int afterPartitionEnd()
{
return dataReader.getPosition() < segmentEnd ? PARTITION_START : advanceSegment();
}
/**
* Enters the next segment that has bytes, as {@code SSTableSimpleScanner.advanceRange} does,
* and leaves the reader at its first partition.
*
* @return PARTITION_START, or DONE when no segment is left
*/
private int advanceSegment()
{
while (segmentIndex < segments.length)
{
PartitionPositionBounds next = segments[segmentIndex++];
if (segmentEnd > next.lowerPosition)
throw new IllegalArgumentException("Ranges supplied to SSTableCursorReader must be non-overlapping and in ascending order.");
if (next.upperPosition < next.lowerPosition)
throw new IllegalArgumentException("A range supplied to SSTableCursorReader ends before it starts: "
+ next.lowerPosition + " > " + next.upperPosition);
// An empty range carries no partition. Skip it WITHOUT touching segmentStart, segmentEnd
// or the byte accounting: bytesRead() is bytesReadInPreviousSegments plus the progress
// through the current segment, so moving those to a range the reader never visits makes
// the count go backwards. The scanner avoids this by seeking to the empty range's start;
// not seeking is cheaper and reads nothing outside a range this cursor covers.
if (next.lowerPosition == next.upperPosition)
continue;
bytesReadInPreviousSegments += segmentEnd - segmentStart;
segmentStart = next.lowerPosition;
segmentEnd = next.upperPosition;
try
{
seekPartition(segmentStart);
}View on GitHub (pinned to 88fd0f6a0e)