apache/iceberg · warning
Exception planning scan for
Error message
Exception planning scan for {} at {} What it means
A WARN log emitted when the MetadataTablePlanner operator fails to plan a scan over a metadata table (e.g. all_manifests, all_data_files) used by the orphan-file/rewrite task. After refreshing the table, split planning via FlinkSplitPlanner threw; the exception is sent to the DeleteOrphanFiles ERROR_STREAM and the errorCounter is incremented, so the task can be marked failed downstream.
Solutions
- Check the underlying exception in the side output for unsupported-format or corrupt-file clues; upgrade the Iceberg runtime if the table uses a newer format version.
- Verify all manifests/metadata files are readable with the job's credentials; repair with orphan cleanup or restore metadata if corrupted.
- Avoid running concurrent expire/orphan jobs that delete metadata while the planner reads it.
- Rerun the maintenance task — planning is retryable once the transient I/O issue clears.
Example fix
// before: plan directly against possibly-stale concurrent state table.refresh(); planIcebergSourceSplits(table, scanContext, workerPool); // after: serialize maintenance runs / pin snapshot before planning Table stable = table; stable.refresh(); scanContext = scanContext.useSnapshot(stable.currentSnapshot().snapshotId());
Defensive patterns
Strategy: retry
Validate before calling
// refresh and confirm the table is readable before planning
table.refresh();
if (table.currentSnapshot() == null && table.schema().columns().isEmpty()) {
throw new IllegalStateException("Table metadata unreadable, aborting scan planning");
} Type guard
boolean plannable(Table table) {
try { table.refresh(); return table.operations().current() != null; }
catch (RuntimeException e) { return false; }
} Try / catch
try {
FlinkSplitPlanner.planIcebergSourceSplits(table, scanContext, workerPool);
} catch (Exception e) {
ctx.output(DeleteOrphanFiles.ERROR_STREAM, e);
errorCounter.inc();
} Prevention
- Keep the Iceberg runtime version >= the format version of tables being scanned.
- Do not run expire/orphan jobs concurrently with metadata-table scans.
- Ensure manifests are readable (permissions, object-store availability) before planning.
- Consume the ERROR_STREAM and retry failed maintenance runs after transient I/O issues.
When it happens
Trigger: processElement(): table.refresh() or FlinkSplitPlanner.planIcebergSourceSplits(table, scanContext, workerPool) throws — schema/format incompatibility, corrupt metadata, planning-time I/O reading manifests, or worker-pool issues.
Common situations: Reading metadata of a table written by a newer Iceberg version (unsupported spec/manifest format); corrupt or truncated manifest/metadata files; S3/HDFS read errors while opening manifests; table concurrently expiring snapshots so metadata disappears mid-plan.
Understand the failure class
Background: Database query failed: Internal Server Error 500s wrapping SQL, Prisma, and connection failures — what to check first — this error's family across 16 libraries.
Related errors
- Exception planning scan for
- Exception planning scan for
- Exception listing files for
- Exception listing files for
- Exception listing files for
AI-assisted analysis of apache/iceberg@86d9c8fc54 (2026-09-12).
Data as JSON: /api/errors/54070190198c2340.
Report an issue: GitHub.
Appendix: source
Thrown at flink/v2.1/flink/src/main/java/org/apache/iceberg/flink/maintenance/operator/MetadataTablePlanner.java:101
ThreadPools.newFixedThreadPool(table.name() + "-table-planner", workerPoolSize);
this.splitSerializer = new IcebergSourceSplitSerializer(scanContext.caseSensitive());
this.errorCounter =
TableMaintenanceMetrics.groupFor(
getRuntimeContext(), originalTable.name(), taskName, taskIndex)
.counter(TableMaintenanceMetrics.ERROR_COUNTER);
}
@Override
public void processElement(Trigger trigger, Context ctx, Collector<SplitInfo> out)
throws Exception {
try {
table.refresh();
for (IcebergSourceSplit split :
FlinkSplitPlanner.planIcebergSourceSplits(table, scanContext, workerPool)) {
out.collect(new SplitInfo(splitSerializer.getVersion(), splitSerializer.serialize(split)));
}
} catch (Exception e) {
LOG.warn("Exception planning scan for {} at {}", table, ctx.timestamp(), e);
ctx.output(DeleteOrphanFiles.ERROR_STREAM, e);
errorCounter.inc();
}
}
@Override
public void close() throws Exception {
super.close();
tableLoader.close();
if (workerPool != null) {
workerPool.shutdown();
}
}
public static class SplitInfo {
private final int version;
private final byte[] split;
View on GitHub (pinned to 86d9c8fc54)