apache/iceberg · error · RuntimeIOException

Failed to close manifest

Error message

Failed to close manifest: %s

What it means

ManifestFilterManager.filterManifest builds a filtered manifest for a delete/cleanup operation and closes the manifest writer in a try/catch. If an IOException occurs during writer finalization (or iterating entries), it is rethrown as RuntimeIOException naming the source manifest. The delete operation cannot produce its filtered manifest.

Solutions

  1. Resolve the underlying storage error from the exception cause
  2. Retry the delete operation — it is safe to retry since no snapshot was committed
  3. Check FileIO permissions and quota for the table's manifest location
Defensive patterns

Strategy: try-catch

Validate before calling

// confirm all manifests referenced by the snapshot are readable
for (ManifestFile m : snapshot.allManifests(table.io())) {
  Preconditions.checkArgument(table.io().newInputFile(m.location()).exists(), "Missing manifest: %s", m.location());
}

Try / catch

try {
  table.deleteFiles()/* or other MergingSnapshotProducer op */;
} catch (RuntimeIOException e) {
  LOG.error("Manifest finalization failed for {}; safe to retry", e.getMessage(), e);
  throw e;
}

Prevention

When it happens

Trigger: Calling MergingSnapshotProducer operations (deleteFiles, RowLevel operations, expire) where filterManifest's writer close fails due to storage errors while writing the filtered manifest for a partition.

Common situations: Object storage transient failures during snapshot commit; credentials expiring during large delete operations; disk/quota exhaustion; network instability to the warehouse.

Understand the failure class

Background: "failed to write file", "Could not save figure", "Error saving remote file" — file write failed: causes and fixes across languages and libraries — this error's family across 38 libraries.

Related errors


AI-assisted analysis of apache/iceberg@86d9c8fc54 (2026-09-12). Data as JSON: /api/errors/ae6b2662c674764a. Report an issue: GitHub.

Appendix: source

Thrown at core/src/main/java/org/apache/iceberg/ManifestFilterManager.java:397

    }

    try (ManifestReader<F> reader = newManifestReader(manifest)) {
      PartitionSpec spec = reader.spec();
      PartitionAndMetricsEvaluator evaluator =
          new PartitionAndMetricsEvaluator(tableSchema, spec, deleteExpression);
      // this assumes that the manifest doesn't have files to remove and streams through the
      // manifest without copying data. if a manifest does have a file to remove, this will break
      // out of the loop and move on to filtering the manifest.
      if (manifestHasDeletedFiles(evaluator, manifest, reader)) {
        ManifestFile filtered = filterManifestWithDeletedFiles(evaluator, manifest, reader);
        replacedManifestsCount.incrementAndGet();
        return filtered;
      } else {
        filteredManifests.put(manifest, manifest);
        return manifest;
      }
    } catch (IOException e) {
      throw new RuntimeIOException(e, "Failed to close manifest: %s", manifest);
    }
  }

  private boolean canContainDeletedFiles(ManifestFile manifest, boolean trustManifestReferences) {
    if (hasNoLiveFiles(manifest)) {
      return false;
    }

    if (trustManifestReferences) {
      return manifestsWithDeletes.contains(manifest.path());
    }

    return canContainDroppedFiles(manifest)
        || canContainExpressionDeletes(manifest)
        || canContainDroppedPartitions(manifest);
  }

  private boolean hasNoLiveFiles(ManifestFile manifest) {

View on GitHub (pinned to 86d9c8fc54)