prestodb/presto · error · java.lang.IndexOutOfBoundsException

Invalid channel %d in page with %s channels

Error message

Invalid channel %d in page with %s channels

What it means

Page.dropColumn removes the channel at channelIndex; the index must be within [0, channelCount). Out-of-range values would otherwise corrupt the block array copy, so the library throws IndexOutOfBoundsException.

Source

Thrown at presto-common/src/main/java/com/facebook/presto/common/Page.java:435

    }

    public Page prependColumn(Block column)
    {
        if (column.getPositionCount() != positionCount) {
            throw new IllegalArgumentException(String.format("Column does not have same position count (%s) as page (%s)", column.getPositionCount(), positionCount));
        }

        Block[] result = new Block[blocks.length + 1];
        result[0] = column;
        System.arraycopy(blocks, 0, result, 1, blocks.length);

        return wrapBlocksWithoutCopy(positionCount, result);
    }

    public Page dropColumn(int channelIndex)
    {
        if (channelIndex < 0 || channelIndex >= getChannelCount()) {
            throw new IndexOutOfBoundsException(format("Invalid channel %d in page with %s channels", channelIndex, getChannelCount()));
        }

        Block[] result = new Block[getChannelCount() - 1];
        System.arraycopy(blocks, 0, result, 0, channelIndex);
        System.arraycopy(blocks, channelIndex + 1, result, channelIndex, getChannelCount() - channelIndex - 1);
        return wrapBlocksWithoutCopy(positionCount, result);
    }

    private long updateRetainedSize()
    {
        AtomicLong retainedSizeInBytes = new AtomicLong(INSTANCE_SIZE + sizeOf(blocks));
        Set<Object> referenceSet = newSetFromMap(new IdentityHashMap<>());
        for (Block block : blocks) {
            block.retainedBytesForEachPart((object, size) -> {
                if (referenceSet.add(object)) {
                    retainedSizeInBytes.addAndGet(size);
                }
            });

View on GitHub (pinned to 55bb57d202)

Solutions

  1. Check 0 <= channelIndex < page.getChannelCount() before calling dropColumn
  2. Track which columns have already been dropped to avoid double-drop
  3. Derive channel indexes from the actual page schema (e.g. by locating the column by type/name) instead of hardcoding

Example fix

// before
page.dropColumn(rowIdChannel);
page.dropColumn(rowIdChannel); // second call throws
// after
if (rowIdChannel >= 0 && rowIdChannel < page.getChannelCount()) {
    page = page.dropColumn(rowIdChannel);
    rowIdChannel = -1;
}
Defensive patterns

Strategy: type-guard

Validate before calling

if (channelIndex < 0 || channelIndex >= page.getChannelCount()) {
    throw new IllegalArgumentException("cannot drop channel " + channelIndex
        + " from page with " + page.getChannelCount() + " channels");
}
page = page.dropColumn(channelIndex);

Type guard

boolean canDrop(Page page, int channelIndex) {
    return channelIndex >= 0 && channelIndex < page.getChannelCount();
}

Try / catch

try {
    page = page.dropColumn(channelIndex);
} catch (IndexOutOfBoundsException e) {
    if (!e.getMessage().contains("Invalid channel")) throw e;
    return page; // channel already absent
}

Prevention

When it happens

Trigger: Calling page.dropColumn(i) with i < 0 or i >= page.getChannelCount() — e.g. dropping the same column twice, or using a channel index from a stale/different page layout.

Common situations: fillInRowIDs-style pipelines that drop a channel twice; operators hardcoding channel indexes after the page schema changed; dynamic column removal loops without bounds checks.

Related errors


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