apache/iceberg · error · UncheckedIOException

Failed to close scan: ${scan}

Error message

Failed to close scan: ${scan}

What it means

SparkPartitioningAwareScan.tasks() plans scan tasks inside try-with-resources; an IOException while closing the planned task iterable is wrapped as UncheckedIOException with the scan description. This signals the scan failed to complete/close cleanly, typically an underlying FileIO failure.

Source

Thrown at spark/v3.5/spark/src/main/java/org/apache/iceberg/spark/source/SparkPartitioningAwareScan.java:196

  protected synchronized List<T> tasks() {
    if (tasks == null) {
      try (CloseableIterable<? extends ScanTask> taskIterable = scan.planFiles()) {
        List<T> plannedTasks = Lists.newArrayList();

        for (ScanTask task : taskIterable) {
          ValidationException.check(
              taskJavaClass().isInstance(task),
              "Unsupported task type, expected a subtype of %s: %s",
              taskJavaClass().getName(),
              task.getClass().getName());

          plannedTasks.add(taskJavaClass().cast(task));
        }

        this.tasks = plannedTasks;
      } catch (IOException e) {
        throw new UncheckedIOException("Failed to close scan: " + scan, e);
      }
    }

    return tasks;
  }

  @Override
  protected synchronized List<ScanTaskGroup<T>> taskGroups() {
    if (taskGroups == null) {
      if (groupingKeyType().fields().isEmpty()) {
        CloseableIterable<ScanTaskGroup<T>> plannedTaskGroups =
            TableScanUtil.planTaskGroups(
                CloseableIterable.withNoopClose(tasks()),
                adjustSplitSize(tasks(), scan.targetSplitSize()),
                scan.splitLookback(),
                scan.splitOpenFileCost());
        this.taskGroups = Lists.newArrayList(plannedTaskGroups);

View on GitHub (pinned to 86d9c8fc54)

Solutions

  1. Inspect the cause chain for the real FileIO error (missing file, auth, network) and fix it.
  2. Validate table metadata/manifest availability; re-run the query after transient storage issues clear.
  3. If caused by concurrent expiry, avoid expiring snapshots while queries are planning against them.
Defensive patterns

Strategy: try-catch

Validate before calling

// preflight: read table metadata and list manifests before planning
table.refresh();
table.currentSnapshot().allManifests(table.io()); // throws if manifests unreadable

Try / catch

try { scan.tasks(); } catch (UncheckedIOException e) { log.error("scan close failed: {} cause={}", scan, e.getCause()); throw e; }

Prevention

When it happens

Trigger: Calling tasks() during Spark planning when planFiles()/planTasks() close throws IOException — missing manifests, filesystem errors, expired snapshots.

Common situations: Metadata corruption, concurrent snapshot expiry, or transient cloud storage errors while a Spark query plans an Iceberg scan.

Understand the failure class

Background: "failed to read file", EACCES, ENOENT and "could not read <path>" errors: when a program can't read a file from disk — this error's family across 49 libraries.

Related errors


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