prestodb/presto · error · IllegalArgumentException

ids length is less than positionCount

Error message

ids length is less than positionCount

What it means

DictionaryBlock stores ids as an int array with an idsOffset window. The constructor requires that ids.length - idsOffset >= positionCount, i.e. the array must contain at least positionCount entries after the offset. Otherwise the block would read out of bounds, so IllegalArgumentException("ids length is less than positionCount") is thrown.

Source

Thrown at presto-common/src/main/java/com/facebook/presto/common/block/DictionaryBlock.java:106

    }

    public DictionaryBlock(int positionCount, Block dictionary, int[] ids, boolean dictionaryIsCompacted, DictionaryId dictionarySourceId)
    {
        this(0, positionCount, dictionary, ids, dictionaryIsCompacted, dictionarySourceId);
    }

    public DictionaryBlock(int idsOffset, int positionCount, Block dictionary, int[] ids, boolean dictionaryIsCompacted, DictionaryId dictionarySourceId)
    {
        requireNonNull(dictionary, "dictionary is null");
        requireNonNull(ids, "ids is null");

        if (positionCount < 0) {
            throw new IllegalArgumentException("positionCount is negative");
        }

        this.idsOffset = idsOffset;
        if (ids.length - idsOffset < positionCount) {
            throw new IllegalArgumentException("ids length is less than positionCount");
        }

        this.positionCount = positionCount;
        this.dictionary = dictionary;
        this.ids = ids;
        this.dictionarySourceId = requireNonNull(dictionarySourceId, "dictionarySourceId is null");
        this.retainedSizeInBytes = INSTANCE_SIZE + dictionary.getRetainedSizeInBytes() + sizeOf(ids);

        if (dictionaryIsCompacted) {
            this.sizeInBytes = dictionary.getSizeInBytes() + (Integer.BYTES * (long) positionCount);
            this.uniqueIds = dictionary.getPositionCount();
        }
    }

    @Override
    public int getSliceLength(int position)
    {
        return dictionary.getSliceLength(getId(position));

View on GitHub (pinned to 55bb57d202)

Solutions

  1. Ensure ids.length - idsOffset >= positionCount before construction, or throw a clear upstream error
  2. When slicing, compute positionCount from the sliced array length: ids.length - idsOffset
  3. After compaction/compaction logic, rebuild ids (or resize) so it matches the declared positionCount
  4. Check deserialization that the ids payload length matches the page header's position count

Example fix

// before
int[] ids = fullIds; int idsOffset = 100; int positionCount = fullIds.length; // 100 + len > ids.length
new DictionaryBlock(idsOffset, positionCount, dictionary, ids, false, sourceId);
// after
int positionCount = fullIds.length - idsOffset; // consistent with the offset window
checkArgument(fullIds.length - idsOffset >= positionCount);
new DictionaryBlock(idsOffset, positionCount, dictionary, fullIds, false, sourceId);
Defensive patterns

Strategy: validation

Validate before calling

static DictionaryBlock safeDictionaryBlock(int idsOffset, int positionCount, Block dictionary, int[] ids, boolean compacted, DictionaryId sourceId) {
    checkArgument(ids != null && ids.length - idsOffset >= positionCount,
        "ids.length=%s idsOffset=%s positionCount=%s", ids == null ? -1 : ids.length, idsOffset, positionCount);
    return new DictionaryBlock(idsOffset, positionCount, dictionary, ids, compacted, sourceId);
}

Try / catch

try {
    return new DictionaryBlock(idsOffset, positionCount, dictionary, ids, false, sourceId);
} catch (IllegalArgumentException e) {
    if (e.getMessage().contains("ids length is less than positionCount")) {
        // rebuild ids from the dictionary, or fall back to the non-dictionary block
        return dictionary.getLoadedBlock();
    }
    throw e;
}

Prevention

When it happens

Trigger: Constructing DictionaryBlock(idsOffset, positionCount, dictionary, ids, ...) where ids.length - idsOffset < positionCount — e.g. slicing an ids array with an offset but keeping the original full length as positionCount, or truncating ids without reducing positionCount.

Common situations: Region/slice code (getRegion, copyRegion) computing idsOffset and positionCount inconsistently; custom dictionary compaction that reuses a smaller ids array; deserialization mismatch between declared position count and actual payload size.

Related errors


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