apache/iceberg · error · UncheckedIOException

Failed to close changelog scan: ${scan}

Error message

Failed to close changelog scan: ${scan}

What it means

taskGroups() plans changelog tasks via scan.planTasks() inside try-with-resources; if closing the resulting CloseableIterable throws IOException, the planning failure is wrapped as UncheckedIOException with the scan description. The scan itself failed to plan/close cleanly, usually an underlying FileIO error.

Source

Thrown at spark/v3.5/spark/src/main/java/org/apache/iceberg/spark/source/SparkChangelogScan.java:122

  @Override
  public Batch toBatch() {
    return new SparkBatch(
        sparkContext,
        table,
        null != scan ? scan.fileIO() : table::io,
        readConf,
        EMPTY_GROUPING_KEY_TYPE,
        taskGroups(),
        expectedSchema,
        hashCode());
  }

  private List<ScanTaskGroup<ChangelogScanTask>> taskGroups() {
    if (taskGroups == null) {
      try (CloseableIterable<ScanTaskGroup<ChangelogScanTask>> groups = scan.planTasks()) {
        this.taskGroups = Lists.newArrayList(groups);
      } catch (IOException e) {
        throw new UncheckedIOException("Failed to close changelog scan: " + scan, e);
      }
    }

    return taskGroups;
  }

  @Override
  public String description() {
    return String.format(
        Locale.ROOT,
        "%s [fromSnapshotId=%d, toSnapshotId=%d, filters=%s]",
        table,
        startSnapshotId,
        endSnapshotId,
        Spark3Util.describe(filters));
  }

  @Override

View on GitHub (pinned to 86d9c8fc54)

Solutions

  1. Inspect the cause chain for the underlying FileIO/filesystem error and fix that (missing file, credentials, network).
  2. Verify all referenced manifests/snapshots exist and the table metadata is consistent (run validate/expire checks).
  3. Retry the scan; if transient cloud-storage errors recur, add retries or check endpoint/credentials.
Defensive patterns

Strategy: try-catch

Validate before calling

// before streaming: verify table metadata is readable
TableMetadataParser.read(io, metadataLoc); // throws if metadata/manifests unreadable

Try / catch

try { ... } catch (UncheckedIOException e) { log.error("changelog plan failed: {}", e.getCause()); throw e; }

Prevention

When it happens

Trigger: Calling tasks()/taskGroups() on SparkChangelogScan when planTasks() or the closing of the planned task iterable throws IOException (e.g. missing manifest files, filesystem outage).

Common situations: Corrupt or deleted manifest/metadata files, S3/HDFS transient errors, or snapshot expiry racing a changelog scan in Spark structured streaming.

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/2098b60b492314da. Report an issue: GitHub.