apache/iceberg · error · UncheckedIOException
Failed to close manifest reader
Error message
Failed to close manifest reader
What it means
While caching delete-file changes, BaseSnapshot iterates manifests with a ManifestReader in try-with-resources; closing the reader throws an IOException. Iceberg wraps it in UncheckedIOException with message "Failed to close manifest reader" because the failure happens at cleanup time inside a method that cannot throw checked exceptions.
Source
Thrown at core/src/main/java/org/apache/iceberg/BaseSnapshot.java:290
deleteManifests(fileIO), manifest -> Objects.equal(manifest.snapshotId(), snapshotId));
for (ManifestFile manifest : changedManifests) {
try (ManifestReader<DeleteFile> reader =
ManifestFiles.readDeleteManifest(manifest, fileIO, null)) {
for (ManifestEntry<DeleteFile> entry : reader.entries()) {
switch (entry.status()) {
case ADDED:
adds.add(entry.file().copy());
break;
case DELETED:
deletes.add(entry.file().copyWithoutStats());
break;
default:
// ignore existing
}
}
} catch (IOException e) {
throw new UncheckedIOException("Failed to close manifest reader", e);
}
}
this.addedDeleteFiles = adds.build();
this.removedDeleteFiles = deletes.build();
}
private void cacheDataFileChanges(FileIO fileIO) {
Preconditions.checkArgument(fileIO != null, "Cannot cache data file changes: FileIO is null");
ImmutableList.Builder<DataFile> adds = ImmutableList.builder();
ImmutableList.Builder<DataFile> deletes = ImmutableList.builder();
// read only manifests that were created by this snapshot
Iterable<ManifestFile> changedManifests =
Iterables.filter(
dataManifests(fileIO), manifest -> Objects.equal(manifest.snapshotId(), snapshotId));
try (CloseableIterable<ManifestEntry<DataFile>> entries =View on GitHub (pinned to 86d9c8fc54)
Solutions
- Retry after table.refresh(); the failure is usually transient IO
- Verify the manifest files referenced by the snapshot exist and are readable via the FileIO
- Restore metadata from backup if the manifest is truncated/corrupt (e.g. rewrite with the metadata JSON)
- Exclude competing expire/cleanup jobs while incrementally scanning changes
Example fix
// before
List<DeleteFile> adds = snapshot.addedDeleteFiles();
// after
try {
List<DeleteFile> adds = snapshot.addedDeleteFiles();
} catch (UncheckedIOException e) {
table.refresh();
List<DeleteFile> adds = snapshot.addedDeleteFiles(); // retry transient IO
} Defensive patterns
Strategy: try-catch
Validate before calling
ManifestFile m = snapshot.deleteManifests(io).get(0);
if (!io.newInputFile(m.path()).exists()) throw new IllegalStateException("delete manifest missing"); Try / catch
try { snapshot.addedDeleteFiles(); } catch (UncheckedIOException e) { table.refresh(); /* retry or repair metadata */ } Prevention
- Avoid expireSnapshots/cleanup racing with incremental change reads
- Monitor storage for transient errors; configure FileIO retries
- Keep metadata backups for repair after truncated writes
When it happens
Trigger: Calling addedDeleteFiles()/removedDeleteFiles() on a v2 snapshot whose delete manifests cannot be read or closed — network failure to object storage mid-iteration, corrupted/truncated manifest file, or IO backend error on close.
Common situations: S3/GCS transient errors while reading incremental changes; manifests deleted by concurrent cleanup; truncated metadata files after an interrupted write.
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 entries while caching changes
- Failed to close current writer
- RuntimeIOException
- Failed to close manifest list: %s
- Failed to read manifest file: %s
AI-assisted analysis of apache/iceberg@86d9c8fc54 (2026-09-12).
Data as JSON: /api/errors/7882d479d4dab75e.
Report an issue: GitHub.