apache/iceberg · error · UncheckedIOException
Failed to close changelog scan: + scan
Error message
Failed to close changelog scan: + scan
What it means
SparkChangelogScan.taskGroups plans changelog tasks with try-with-resources; if closing the planned task groups' CloseableIterable throws IOException, it is wrapped in UncheckedIOException('Failed to close changelog scan: <scan>'). The planning succeeded but resource cleanup failed, indicating FileIO-level trouble (file system errors while releasing resources).
Source
Thrown at spark/v4.0/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));
}
@OverrideView on GitHub (pinned to 86d9c8fc54)
Solutions
- Inspect the chained IOException for the storage-level cause.
- Retry the scan/stream micro-batch; close-time errors are usually transient.
- Verify storage credentials/refresh (S3 session tokens, HDFS delegation tokens) cover the job duration.
- Check network stability and storage endpoint health for executors.
Example fix
// before
try (CloseableIterable<...> groups = scan.planTasks()) { ... }
// after
try {
try (CloseableIterable<...> groups = scan.planTasks()) { ... }
} catch (UncheckedIOException e) {
if (isTransient(e.getCause())) retry(); else throw e;
} Defensive patterns
Strategy: retry
Validate before calling
// verify storage is reachable before planning FileSystem fs = FileSystem.get(table.location(), hadoopConf); fs.exists(new Path(table.location())); // throws early on connectivity issues
Try / catch
try { planTaskGroups(); } catch (UncheckedIOException e) {
if (isTransientStorageError(e.getCause())) retryWithBackoff(); else throw e;
} Prevention
- Use stable, highly available storage endpoints for warehouse.
- Ensure long-lived credentials (instance profiles, keytabs) instead of short-lived tokens.
- Set storage client timeouts/retries at the FileIO level.
When it happens
Trigger: scan.planTasks() succeeds but the CloseableIterable.close() inside the try-with-resources throws IOException — e.g. HDFS/S3 error during manifest reader close, stream aborted by timeout, or disk/network failure on the executor.
Common situations: Object store throttling or token expiry during task-group close; flaky network between Spark executor and storage; HDFS client cache eviction errors.
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
- Failed to close changelog scan: ${scan}
- Failed to close scan: + scan
- Failed to close changelog scan: ${scan}
- Failed to close scan: ${scan}
- Failed to close task iterable
AI-assisted analysis of apache/iceberg@86d9c8fc54 (2026-09-12).
Data as JSON: /api/errors/79aecb245c674989.
Report an issue: GitHub.