apache/iceberg · error · RuntimeIOException

Failed to close manifest list: %s

Error message

Failed to close manifest list: %s

What it means

During expireSnapshots cleanup, ReachableFileCleanup.pruneReferencedManifests reads each snapshot's manifest list and wraps it in a CloseableIterable. If closing that iterable raises an IOException, it is rethrown as RuntimeIOException naming the manifest list location. The failure is an I/O problem on the manifest list file.

Source

Thrown at core/src/main/java/org/apache/iceberg/ReachableFileCleanup.java:136

        .throwFailureWhenFinished()
        .executeWith(planExecutorService)
        .onFailure(
            (snapshot, exc) ->
                LOG.warn(
                    "Failed to determine manifests for snapshot {}", snapshot.snapshotId(), exc))
        .run(
            snapshot -> {
              try (CloseableIterable<ManifestFile> manifestFiles = readManifests(snapshot)) {
                for (ManifestFile manifestFile : manifestFiles) {
                  candidateSet.remove(manifestFile);
                  if (candidateSet.isEmpty()) {
                    return;
                  }

                  currentManifestCallback.accept(manifestFile.copy());
                }
              } catch (IOException e) {
                throw new RuntimeIOException(
                    e, "Failed to close manifest list: %s", snapshot.manifestListLocation());
              }
            });

    return candidateSet;
  }

  private Set<ManifestFile> readManifests(Set<Snapshot> snapshots) {
    Set<ManifestFile> manifestFiles = ConcurrentHashMap.newKeySet();
    Tasks.foreach(snapshots)
        .retry(3)
        .stopOnFailure()
        .throwFailureWhenFinished()
        .executeWith(planExecutorService)
        .onFailure(
            (snapshot, exc) ->
                LOG.warn(
                    "Failed to determine manifests for snapshot {}", snapshot.snapshotId(), exc))

View on GitHub (pinned to 86d9c8fc54)

Solutions

  1. Verify the manifest list files are readable and the storage backend is healthy, then retry expireSnapshots
  2. Check FileIO credentials/permissions and network stability to the object store
  3. If files were externally deleted, restore consistency (e.g. re-register the table or repair metadata) before cleanup

Example fix

// before
expireSnapshots(table).cleanExpiredFiles(true).execute();
// after
try {
  expireSnapshots(table).cleanExpiredFiles(true).execute();
} catch (RuntimeIOException e) {
  LOG.error("Manifest list I/O failure during cleanup: {}", e.getMessage());
  // check storage health/credentials, then retry
}
Defensive patterns

Strategy: try-catch

Validate before calling

// verify the manifest list is readable before expiration
io.newInputFile(snapshot.manifestListLocation()).exists();

Try / catch

try {
  expireSnapshots(table).cleanExpiredFiles(true).execute();
} catch (RuntimeIOException e) {
  // inspect e.getMessage() for the manifest list location; check storage health, then retry
}

Prevention

When it happens

Trigger: expireSnapshots with file cleanup where closing the manifest-list reader of a snapshot throws IOException — typically underlying FileIO errors such as unavailable storage, network failure, or corrupted/unreadable manifest list object.

Common situations: Object-store throttling or connectivity drops during snapshot expiration; missing/corrupted manifest list files (deleted externally or failed prior write); credential/permission problems on the warehouse path.

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