apache/iceberg · error · RuntimeException

Failed to plan files

Error message

Failed to plan files

What it means

BaseDistributedDataScan.doPlanFiles() throws RuntimeException('Failed to plan files') when a CompletionException escapes the coordinated planning of data and delete futures (e.g. a remote scan task failed or was cancelled). The two futures are cancelled and the underlying exception is attached as the cause. It wraps whatever failure occurred during distributed scan planning.

Solutions

  1. Inspect e.getCause() (the CompletionException cause) for the root failure
  2. Retry the scan planning; transient executor/storage failures may recover
  3. Increase executor thread pool size or timeout budgets for remote planning
  4. Fall back to local planning by tuning planning-mode properties (see shouldPlanLocally)
  5. Check task executor logs in the distributed environment for the original stack

Example fix

// before
CloseableIterable<FileScanTask> tasks = distributedScan.planFiles(); // RuntimeException on remote failure
// after
try {
  tasks = distributedScan.planFiles();
} catch (RuntimeException e) {
  Throwable root = e.getCause() == null ? e : e.getCause();
  LOG.error("Planning failed: {}", root.getMessage(), root);
  tasks = table.newScan().planFiles(); // fall back to local planning
}
Defensive patterns

Strategy: try-catch

Validate before calling

null

Type guard

null

Try / catch

try { tasks = scan.planFiles(); } catch (RuntimeException e) { Throwable cause = e.getCause(); /* CompletionException cause has the root failure */ }

Prevention

When it happens

Trigger: The dataFiles or deletes CompletableFuture completes exceptionally (task executor failure, remote manifest read error, cancellation) while joining results in doPlanFiles.

Common situations: Executor thread death or OOM in a distributed planner; timeouts reading manifests remotely; shutdown of the monitoring pool mid-plan.

Understand the failure class

Background: 'Something went wrong' / 'Request failed (500)' / 'HTTP error! status: 404' — what failed HTTP requests actually mean and how to find the real cause — this error's family across 28 libraries.

Related errors


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

Appendix: source

Thrown at core/src/main/java/org/apache/iceberg/BaseDistributedDataScan.java:183

        newDeletesFuture(deleteManifests, planDeletesLocally, monitorPool);

    CompletableFuture<Iterable<CloseableIterable<DataFile>>> dataFuture =
        newDataFuture(dataManifests, planDataLocally, loadColumnStats, monitorPool);

    try {
      Iterable<CloseableIterable<ScanTask>> fileTasks =
          toFileTasks(dataFuture, deletesFuture, copyDataFiles);

      if (shouldPlanWithExecutor() && (planDataLocally || mayHaveEqualityDeletes)) {
        return new ParallelIterable<>(fileTasks, planExecutor());
      } else {
        return CloseableIterable.concat(fileTasks);
      }

    } catch (CompletionException e) {
      deletesFuture.cancel(true /* may interrupt */);
      dataFuture.cancel(true /* may interrupt */);
      throw new RuntimeException("Failed to plan files", e);

    } finally {
      monitorPool.shutdown();
    }
  }

  @Override
  public CloseableIterable<ScanTaskGroup<ScanTask>> planTasks() {
    return TableScanUtil.planTaskGroups(
        planFiles(), targetSplitSize(), splitLookback(), splitOpenFileCost());
  }

  private List<ManifestFile> findMatchingDataManifests(Snapshot snapshot) {
    List<ManifestFile> dataManifests = snapshot.dataManifests(table().io());
    scanMetrics().totalDataManifests().increment(dataManifests.size());

    List<ManifestFile> matchingDataManifests = filterManifests(dataManifests);
    int skippedDataManifestsCount = dataManifests.size() - matchingDataManifests.size();

View on GitHub (pinned to 86d9c8fc54)