prestodb/presto · error · IllegalArgumentException

offset must be 0 or 8

Error message

offset must be 0 or 8

What it means

An Int128 value is 16 bytes stored as two longs. getLong(position, offset) only accepts offsets of 0 (low 64 bits) or 8 (high 64 bits); any other offset would read unaligned or out-of-value data, so IllegalArgumentException is thrown.

Source

Thrown at presto-common/src/main/java/com/facebook/presto/common/block/Int128ArrayBlock.java:143

        consumer.accept(values, sizeOf(values));
        if (valueIsNull != null) {
            consumer.accept(valueIsNull, sizeOf(valueIsNull));
        }
        consumer.accept(this, INSTANCE_SIZE);
    }

    @Override
    public int getPositionCount()
    {
        return positionCount;
    }

    @Override
    public long getLong(int position, int offset)
    {
        checkReadablePosition(position);
        if (offset != 0 && offset != 8) {
            throw new IllegalArgumentException("offset must be 0 or 8");
        }
        return getLongUnchecked(position + positionOffset, offset);
    }

    /**
     * Get the Slice starting at {@code this.positionOffset + offset} in the value at {@code position} with {@code length} bytes.
     *
     * @param position The logical position of the 128-bit integer in the values array.
     *                 For example, position = 0 refers to the 128-bit integer at values[2] and values[3] if this.positionOffset = 1.
     * @param offset The offset to the position in the unit of 128-bit integers.
     *               For example, offset = 1 means the next position (one 128-bit integer or 16 bytes) to the specified position.
     *               This means we always compare bytes starting at 128-bit integer boundaries.
     * @param length The length in bytes. It has to be a multiple of 16.
     */
    @Override
    public Slice getSlice(int position, int offset, int length)
    {
        checkValidRegion(positionCount, offset, length / SIZE_OF_LONG / 2);

View on GitHub (pinned to 55bb57d202)

Solutions

  1. Restrict the calling code to offsets 0 and 8 when the column type is 128-bit.
  2. Type-dispatch before reading: check the block type and use getRawSlice/getLongUnchecked-style access or Int128-specific helpers for generic iteration.
  3. Guard the call site: if (offset != 0 && offset != 8) handle via a 16-byte-aware path.

Example fix

// before
long value = block.getLong(position, offset); // offset arbitrary
// after
checkArgument(offset == 0 || offset == 8, "INT128 supports offsets 0/8 only, got %s", offset);
long value = block.getLong(position, offset);
Defensive patterns

Strategy: type-guard

Validate before calling

if (type instanceof DecimalType && ((DecimalType) type).isShortNotUsed()) {
    checkArgument(offset == 0 || offset == 8, "INT128 getLong supports offsets 0/8 only, got %s", offset);
}

Type guard

boolean isInt128SafeOffset(int offset) { return offset == 0 || offset == 8; }

Try / catch

try {
    value = block.getLong(position, offset);
} catch (IllegalArgumentException e) {
    if (e.getMessage().contains("offset must be 0 or 8")) {
        throw new IllegalStateException("generic 8-byte read applied to 128-bit column at offset " + offset, e);
    }
    throw e;
}

Prevention

When it happens

Trigger: Calling Int128ArrayBlock.getLong(position, offset) with an offset other than 0 or 8 — e.g. generic code that assumes 8-byte fixed-width types and passes arbitrary byte offsets like 4, or loops offsetting by SIZE_OF_LONG beyond the two allowed values.

Common situations: Generic vectorized operators (hash, projection, aggregation) written for BIGINT being reused on DECIMAL(38,x)/INT128 columns; copy loops that walk past the 16-byte value boundary.

Related errors


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