apache/iceberg · error · UncheckedIOException
Failed to close table loader
Error message
Failed to close table loader
What it means
IcebergSource.planSplitsForBatch plans batch splits inside a try-with-resources on the TableLoader; an IOException during closing the table loader is wrapped in UncheckedIOException with the (arguably misleading) message 'Failed to close table loader'. The failure occurs during batch split discovery when the loader's IO cannot be cleanly closed after planning.
Source
Thrown at flink/v1.20/flink/src/main/java/org/apache/iceberg/flink/source/IcebergSource.java:166
*/
private List<IcebergSourceSplit> planSplitsForBatch(String threadName) {
if (batchSplits != null) {
return batchSplits;
}
ExecutorService workerPool =
ThreadPools.newFixedThreadPool(threadName, scanContext.planParallelism());
try (TableLoader loader = tableLoader.clone()) {
loader.open();
this.batchSplits =
FlinkSplitPlanner.planIcebergSourceSplits(loader.loadTable(), scanContext, workerPool);
LOG.info(
"Discovered {} splits from table {} during job initialization",
batchSplits.size(),
tableName);
return batchSplits;
} catch (IOException e) {
throw new UncheckedIOException("Failed to close table loader", e);
} finally {
workerPool.shutdown();
}
}
@Override
public Boundedness getBoundedness() {
return scanContext.isStreaming() ? Boundedness.CONTINUOUS_UNBOUNDED : Boundedness.BOUNDED;
}
@Override
public SourceReader<T, IcebergSourceSplit> createReader(SourceReaderContext readerContext) {
IcebergSourceReaderMetrics metrics =
new IcebergSourceReaderMetrics(readerContext.metricGroup(), tableName);
return new IcebergSourceReader<>(
emitter, metrics, readerFunction, splitComparator, readerContext);
}
View on GitHub (pinned to 86d9c8fc54)
Solutions
- Check the chained IOException cause to see whether the real failure is close() or the planning step
- Upgrade Iceberg — loader close failures are often fixed in newer versions' FileIO implementations
- If using a custom TableLoader, make close() idempotent and null-safe
- Retry job startup; investigate storage connectivity if close failures recur
Example fix
// before
FlinkSource.forRowData().tableLoader(customLoader).build(); // customLoader.close() throws
// after: make close idempotent
public void close() {
if (!closed) {
closed = true;
io.close();
}
} Defensive patterns
Strategy: try-catch
Validate before calling
// ensure the custom loader closes cleanly TableLoader loader = TableLoader.fromCatalog(...); loader.open(); loader.loadTable(); loader.close();
Try / catch
try {
source = IcebergSource.forRowData().tableLoader(loader).build();
} catch (UncheckedIOException e) {
LOG.error("Table loader lifecycle failure", e.getCause());
throw e;
} Prevention
- Use stock TableLoader implementations where possible
- Make custom TableLoader.close() idempotent
- Upgrade Iceberg if close-time IOExceptions appear in FileIO implementations
When it happens
Trigger: Batch mode IcebergSource split planning where TableLoader.close() throws IOException (e.g. underlying FileSystem/FileIO close fails, cached connections broken), or an earlier IOException from planning propagated through the resource-close path.
Common situations: Hadoop FileSystem cache/close issues, storage clients with leaked connections after long-running jobs, custom TableLoader implementations throwing on close.
Related errors
- Failed to load iceberg table from table loader: ${tableLoade
- Failed to load iceberg table from table loader: ${tableLoade
- Failed to create iceberg input splits for table:
- Failed to load iceberg table from table loader:
- Failed to load iceberg table from table loader: ${tableLoade
AI-assisted analysis of apache/iceberg@86d9c8fc54 (2026-09-12).
Data as JSON: /api/errors/19c596be9687c084.
Report an issue: GitHub.