apache/iceberg · error · RuntimeIOException
Failed to close
Error message
Failed to close
What it means
DeleteFileIndex.loadDeleteFiles reads manifest entries inside Tasks.foreach with a CloseableIterable; an IOException while closing/reading the iterable during parallel loading is wrapped in RuntimeIOException with message 'Failed to close'. The deletes index therefore fails to build.
Source
Thrown at core/src/main/java/org/apache/iceberg/DeleteFileIndex.java:516
.throwFailureWhenFinished()
.executeWith(executorService)
.run(
deleteFile -> {
try (CloseableIterable<ManifestEntry<DeleteFile>> reader = deleteFile) {
for (ManifestEntry<DeleteFile> entry : reader) {
if (entry.dataSequenceNumber() > minSequenceNumber) {
DeleteFile file = entry.file();
// keep minimum stats to avoid memory pressure
Set<Integer> columns =
file.content() == FileContent.POSITION_DELETES
? Set.of(MetadataColumns.DELETE_FILE_PATH.fieldId())
: Set.copyOf(file.equalityFieldIds());
// copy with stats for better filtering against data file stats
files.add(ContentFileUtil.copy(file, true, columns));
}
}
} catch (IOException e) {
throw new RuntimeIOException(e, "Failed to close");
}
});
return files;
}
private Collection<Schema> schemas() {
if (schemasById != null) {
return schemasById.values();
} else {
return specsById.values().stream().map(PartitionSpec::schema).collect(Collectors.toList());
}
}
DeleteFileIndex build() {
Map<Integer, Types.NestedField> fieldsById = Schema.indexFields(schemas());
Function<Integer, Types.NestedField> fieldLookup = fieldsById::get;
Iterable<DeleteFile> files = deleteFiles != null ? filterDeleteFiles() : loadDeleteFiles();
View on GitHub (pinned to 86d9c8fc54)
Solutions
- Inspect the cause (e.getCause()) for the underlying IOException and fix storage access/credentials.
- Retry the scan; transient cloud storage errors often resolve.
- Ensure snapshot expiry is not racing with scans; stop expiry during active query windows.
- Verify delete manifests referenced by the snapshot still exist and are readable.
Example fix
// before
CloseableIterable<...> items = ...; // leaked, failure occurs on implicit close
// after
try (CloseableIterable<...> items = ...) {
... // ensure proper resource handling around manifest reads
} Defensive patterns
Strategy: retry
Validate before calling
// before scanning, verify delete manifests are present
table.operations().current().currentSnapshot().dataManifests(table.io())
.forEach(m -> Preconditions.check(table.io().newInputFile(m.path()).exists(), "missing " + m.path())); Try / catch
try { CloseableIterable<DeleteFile> f = index.files(); } catch (RuntimeIOException e) {
LOG.error("delete index load failed", e.getCause());
throw e;
} Prevention
- Avoid concurrent snapshot expiration during scans.
- Use retrying object-store clients for transient IO errors.
- Monitor manifest lifecycle vs. long-running scans.
When it happens
Trigger: Building a DeleteFileIndex (e.g. via files() during a scan) where reading or closing a delete-manifest stream throws IOException — storage outage, deleted/missing manifest, credentials revoked mid-scan.
Common situations: S3/GCS transient errors during large delete-index loads; concurrent snapshot expiration deleting manifests while a scan plans; misconfigured FileIO credentials.
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
- Unsupported content:
- Failed to write delete manifests
- Failed to close dvFileWriter
- Failed to close iterable
- Failed to create file: %s
AI-assisted analysis of apache/iceberg@86d9c8fc54 (2026-09-12).
Data as JSON: /api/errors/bf6712c9f28762ca.
Report an issue: GitHub.