prestodb/presto · error · IllegalStateException

was used before initialization

Error message

 was used before initialization

What it means

VariableWidthBlockBuilder lazily allocates its internal arrays (valueIsNull, offsets, sliceOutput) in initializeCapacity. This IllegalStateException is thrown when the builder still has buffered state (positions or currentEntrySize non-zero) but initializeCapacity is called again, indicating the builder was used without being properly reset/re-initialized after prior writes.

Source

Thrown at presto-common/src/main/java/com/facebook/presto/common/block/VariableWidthBlockBuilder.java:321

        positions++;

        if (blockBuilderStatus != null) {
            blockBuilderStatus.addBytes(SIZE_OF_BYTE + SIZE_OF_INT + bytesWritten);
        }
    }

    private void growCapacity()
    {
        int newSize = BlockUtil.calculateNewArraySize(valueIsNull.length);
        valueIsNull = Arrays.copyOf(valueIsNull, newSize);
        offsets = Arrays.copyOf(offsets, newSize + 1);
        updateArraysDataSize();
    }

    private void initializeCapacity()
    {
        if (positions != 0 || currentEntrySize != 0) {
            throw new IllegalStateException(getClass().getSimpleName() + " was used before initialization");
        }
        initialized = true;
        valueIsNull = new boolean[initialEntryCount];
        offsets = new int[initialEntryCount + 1];
        sliceOutput = new DynamicSliceOutput(initialSliceOutputSize);
        updateArraysDataSize();
    }

    private void updateArraysDataSize()
    {
        arraysRetainedSizeInBytes = sizeOf(valueIsNull) + sizeOf(offsets);
    }

    @Override
    public BlockBuilder readPositionFrom(SliceInput input)
    {
        boolean isNull = input.readByte() == 0;
        if (isNull) {

View on GitHub (pinned to 55bb57d202)

Solutions

  1. Call reset() only after the current entry is closed (entryEnded), or create a new VariableWidthBlockBuilder instead of reusing one
  2. Ensure each write sequence is completed with closeEntry()/entryEnded before further lifecycle calls
  3. If pooling builders, reset them at the start of each use and verify positions==0 and currentEntrySize==0
  4. Check for double initialization paths in custom block builder subclasses

Example fix

// before
builder.writeBytes(slice, 0, len); // builder still holds an open entry from previous use
// after
builder.reset(); // only when positions == 0 && currentEntrySize == 0
builder.writeBytes(slice, 0, len);
Defensive patterns

Strategy: validation

Validate before calling

if (builder.getPositions() != 0 || /* currentEntrySize not observable; assume dirty */ false) {
    builder = new VariableWidthBlockBuilder(null, expectedEntries, expectedBytes);
}

Type guard

boolean isReusable(VariableWidthBlockBuilder b) { return b.getPositions() == 0; }

Try / catch

try { builder.writeBytes(slice, offset, length); } catch (IllegalStateException e) { builder = new VariableWidthBlockBuilder(null, initialEntryCount, initialSize); builder.writeBytes(slice, offset, length); }

Prevention

When it happens

Trigger: Calling a write method (writeByte, writeShort, writeInt, writeLong, writeBytes) or entryAdded on a builder whose internal state was already partially advanced (positions != 0 or currentEntrySize != 0), e.g. reusing a builder after writes without reset(), or a lifecycle bug where entryEnded/closeEntry was never called so state remains dirty.

Common situations: Reusing a pooled/cached BlockBuilder across queries without reset(); calling reset() when an entry write is still open (currentEntrySize > 0); custom code that copies builder state and calls initializeCapacity directly via reflection or subclassing.

Related errors


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