apache/iceberg · warning · UncheckedIOException

Failed to close table loader

Error message

Failed to close table loader

What it means

IcebergSource.planSplitsForBatch plans all batch splits during job initialization inside a try-with-resources on the TableLoader; if closing the TableLoader throws IOException, it is wrapped into an UncheckedIOException with this message. The worker pool is shut down in a finally block regardless. It indicates a failure releasing the table's FileIO resources after batch planning completed.

Solutions

  1. Inspect the wrapped IOException cause; it usually reflects an underlying filesystem close problem that is often benign — verify planning output was produced.
  2. Check Hadoop/HDFS client configuration and versions for known close/handle-leak bugs; align Hadoop client versions.
  3. Avoid forced cancellation during initialization; let batch planning and close finish.
  4. If reproducible, report with the cause stack; the planned splits are valid, so a retry typically succeeds.
Defensive patterns

Strategy: try-catch

Try / catch

try {
  List<IcebergSourceSplit> splits = source.splits(...);
} catch (UncheckedIOException e) {
  if (e.getCause() != null && isBenignCloseFailure(e.getCause())) {
    LOG.warn("Table loader close failed after successful planning; ignoring", e);
  } else { throw e; }
}

Prevention

When it happens

Trigger: Calling IcebergSource.splits (batch mode) where tableLoader.close() throws IOException after batch split discovery succeeds — e.g., Hadoop FileIO close failing on the underlying filesystem handle.

Common situations: Hadoop FileSystem close errors in long-lived jobs; interrupted close during job cancellation; misconfigured Hadoop filesystem caches releasing native resources.

Understand the failure class

Background: "open() failed", "failed to open file", "cannot create file" — what a file open error means and how to fix it — this error's family across 42 libraries.

Related errors


AI-assisted analysis of apache/iceberg@86d9c8fc54 (2026-09-12). Data as JSON: /api/errors/775c9bbf728eba07. Report an issue: GitHub.

Appendix: source

Thrown at flink/v2.2/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)