prestodb/presto · error · IllegalArgumentException

positionCount is negative

Error message

positionCount is negative

What it means

The DictionaryBlock constructor validates that positionCount is non-negative before using it. A negative positionCount would make the block report an impossible number of positions and break all downstream block math (offsets, slices, copyRegion), so IllegalArgumentException("positionCount is negative") is thrown eagerly.

Source

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

    }

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

    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();
        }
    }

View on GitHub (pinned to 55bb57d202)

Solutions

  1. Clamp or validate positionCount before constructing: checkState(positionCount >= 0)
  2. Fix the offset computation that produced the negative value (check subtraction order and source metadata)
  3. Verify serialized page data is not corrupted (checksum/version mismatch)
  4. Ensure deserialization code reads the count field with the correct endianness/width

Example fix

// before
int positionCount = endOffset - startOffset; // can be negative on corrupt metadata
new DictionaryBlock(0, positionCount, dictionary, ids, false, dictionarySourceId);
// after
int positionCount = endOffset - startOffset;
checkArgument(positionCount >= 0, "Invalid positionCount %s computed from offsets", positionCount);
new DictionaryBlock(0, positionCount, dictionary, ids, false, dictionarySourceId);
Defensive patterns

Strategy: validation

Validate before calling

static DictionaryBlock safeDictionaryBlock(int idsOffset, int positionCount, Block dictionary, int[] ids) {
    checkArgument(positionCount >= 0, "positionCount must be >= 0, got %s", positionCount);
    checkArgument(ids.length - idsOffset >= positionCount, "ids too short");
    return new DictionaryBlock(idsOffset, positionCount, dictionary, ids, false, DictionaryId.randomDictionaryId());
}

Try / catch

try {
    return new DictionaryBlock(idsOffset, positionCount, dictionary, ids, false, sourceId);
} catch (IllegalArgumentException e) {
    throw new dataCorruptionException("Bad dictionary block dimensions: " + e.getMessage(), e);
}

Prevention

When it happens

Trigger: Constructing DictionaryBlock directly or via factory methods with an int positionCount < 0 — typically from a bad length computed earlier (e.g. endOffset - startOffset underflow), a deserialized header with a corrupt count, or an arithmetic bug.

Common situations: Custom readers computing position counts from file metadata (Parquet/ORC num_values underflow); page serialization corruption; subtraction of offsets in the wrong order producing negative lengths.

Related errors


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