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
- Ensure ids.length - idsOffset >= positionCount before construction, or throw a clear upstream error
- When slicing, compute positionCount from the sliced array length: ids.length - idsOffset
- After compaction/compaction logic, rebuild ids (or resize) so it matches the declared positionCount
- 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
- Compute positionCount as ids.length - idsOffset when slicing
- Keep idsOffset, ids array, and positionCount derived from the same source region
- Validate deserialized ids payload size against the header position count
- Add unit tests for region/slice paths that adjust idsOffset
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
- positionCount is negative
- array1 and array2 cannot be null and should have same length
- arrayOffset is negative
- positionCount is negative
- New column does not have same number of rows as old column
AI-assisted analysis of prestodb/presto@55bb57d202 (2026-09-04).
Data as JSON: /api/errors/687d41669ae7a8b6.
Report an issue: GitHub.