prestodb/presto · error · IllegalArgumentException

position is not valid:

Error message

position is not valid: 

What it means

RunLengthEncodedBlock.checkReadablePosition validates that a requested position lies within [0, positionCount). All primitive accessors (getLong, getInt, getSlice, etc.) route through it. Because all positions read the same underlying value, only the outer bounds matter — but an out-of-range index means the caller is addressing positions that do not exist in this block.

Source

Thrown at presto-common/src/main/java/com/facebook/presto/common/block/RunLengthEncodedBlock.java:343

    {
        return format("RunLengthEncodedBlock(%d){positionCount=%d,value=%s}", hashCode(), getPositionCount(), value);
    }

    @Override
    public Block getLoadedBlock()
    {
        Block loadedValueBlock = value.getLoadedBlock();

        if (loadedValueBlock == value) {
            return this;
        }
        return new RunLengthEncodedBlock(loadedValueBlock, positionCount);
    }

    private void checkReadablePosition(int position)
    {
        if (position < 0 || position >= positionCount) {
            throw new IllegalArgumentException("position is not valid: " + position);
        }
    }

    @Override
    public byte getByteUnchecked(int internalPosition)
    {
        assert internalPositionInRange(internalPosition, getOffsetBase(), getPositionCount());
        return value.getByte(0);
    }

    @Override
    public short getShortUnchecked(int internalPosition)
    {
        assert internalPositionInRange(internalPosition, getOffsetBase(), getPositionCount());
        return value.getShort(0);
    }

    @Override

View on GitHub (pinned to 55bb57d202)

Solutions

  1. Bounds-check position against the current block's getPositionCount() before access
  2. Re-fetch the block/position count after any transform instead of caching stale counts
  3. Fix loop conditions to position < positionCount (not <=)
  4. Ensure the position passed belongs to the same block instance being read

Example fix

// before
for (int i = 0; i <= block.getPositionCount(); i++) { // off-by-one
    long v = block.getLong(i, 0);
}
// after
for (int i = 0; i < block.getPositionCount(); i++) {
    long v = block.getLong(i, 0);
}
Defensive patterns

Strategy: validation

Validate before calling

// before reading any position from an RLE block
if (position < 0 || position >= block.getPositionCount()) {
    throw new IllegalArgumentException("position " + position + " out of range for block with " + block.getPositionCount() + " positions");
}
long v = block.getLong(position, 0);

Try / catch

try {
    return block.getLong(position, 0);
} catch (IllegalArgumentException e) {
    if (e.getMessage().startsWith("position is not valid")) {
        throw new IllegalStateException("stale position " + position + " vs current count " + block.getPositionCount() + "; refresh block reference", e);
    }
    throw e;
}

Prevention

When it happens

Trigger: Reading block position >= getPositionCount() or < 0, e.g. iterating with a stale position count after a block was compacted/filtered, or indexing with a source position from a different block.

Common situations: Operators holding an old positionCount snapshot after page slicing; off-by-one loops (position <= count); copying positions across blocks with different sizes during page reshuffling.

Related errors


AI-assisted analysis of prestodb/presto@55bb57d202 (2026-09-04). Data as JSON: /api/errors/14cbe79db48ed9a2. Report an issue: GitHub.