prestodb/presto · error · PrestoException

CORRUPT_PAGE

CORRUPT_PAGE

Error message

delegate page channels do not match expected number of channels

What it means

The ChangelogPageSource's ROWDATA column converter expects the delegate page's channel count to exactly equal the number of fields in the changelog's inner row type, since channels are wrapped into a RowBlock. A mismatch means the underlying page doesn't correspond to the declared row schema, so CORRUPT_PAGE is thrown to avoid producing a malformed row block.

Source

Thrown at presto-iceberg/src/main/java/com/facebook/presto/iceberg/changelog/ChangelogPageSource.java:130

        return delegate.getSystemMemoryUsage();
    }

    @Override
    public void close()
            throws IOException
    {
        delegate.close();
    }

    public enum ChangelogSchemaColumns
    {
        OPERATION((source, page) -> RunLengthEncodedBlock.create(VARCHAR, Slices.utf8Slice(source.changelogSplitInfo.getOperation().toString()), page.getPositionCount())),
        ORDINAL((source, page) -> RunLengthEncodedBlock.create(BIGINT, source.changelogSplitInfo.getOrdinal(), page.getPositionCount())),
        SNAPSHOTID((source, page) -> RunLengthEncodedBlock.create(BIGINT, source.changelogSplitInfo.getSnapshotId(), page.getPositionCount())),

        ROWDATA((source, page) -> {
            if (source.innerRowType.getFields().size() != page.getChannelCount()) {
                throw new PrestoException(CORRUPT_PAGE, "delegate page channels do not match expected number of channels");
            }
            Block[] channels = new Block[page.getChannelCount()];
            for (int i = 0; i < page.getChannelCount(); i++) {
                channels[i] = page.getBlock(i);
            }
            return RowBlock.fromFieldBlocks(page.getPositionCount(), Optional.empty(), channels);
        });

        private final ChangelogUtil.Function2<ChangelogPageSource, Page, Block> blockSupplier;

        ChangelogSchemaColumns(ChangelogUtil.Function2<ChangelogPageSource, Page, Block> blockSupplier)
        {
            this.blockSupplier = blockSupplier;
        }

        public Block getBlock(ChangelogPageSource pageSource, Page page)
        {
            return blockSupplier.apply(pageSource, page);

View on GitHub (pinned to 55bb57d202)

Solutions

  1. Re-run the query so splits are re-planned against the current table schema.
  2. Verify the changelog split's innerRowType matches the actual file schema; fix metadata caching if it doesn't.
  3. Check for schema-evolution operations (ADD/DROP COLUMN) concurrent with the changelog query.

Example fix

// before: channels from stale schema
RowBlock.fromFieldBlocks(page.getPositionCount(), Optional.empty(), channels);
// after: guard before building
if (source.innerRowType.getFields().size() != page.getChannelCount()) {
    throw new PrestoException(CORRUPT_PAGE, "delegate page channels do not match expected number of channels");
}
Defensive patterns

Strategy: retry

Type guard

boolean pageMatchesRowType(Page page, RowType innerRowType) {
    return page.getChannelCount() == innerRowType.getFields().size();
}

Try / catch

try { return pageSource.getNextPage(); } catch (PrestoException e) { if (e.getErrorCode() == CORRUPT_PAGE.toErrorCode()) { /* re-plan split against current schema */ } throw e; }

Prevention

When it happens

Trigger: getNextPage on a changelog split where source.innerRowType.getFields().size() differs from page.getChannelCount() — e.g. stale split metadata after schema evolution, or a delegate source emitting extra/missing columns.

Common situations: Iceberg table schema changed (columns added/dropped) between split planning and execution; reading a changelog with an outdated cached schema.

Related errors


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