apache/beam · error · RuntimeException

Failed to read delete manifest

Error message

Failed to read delete manifest: ${manifest.path()}

What it means

This method reads a delete manifest and copies applicable delete files (with reduced columns for the relevant equality/delete path fields). Any exception while iterating manifest entries is wrapped in a RuntimeException naming the manifest path.

Solutions

  1. Open the manifest at the reported path directly to confirm it exists and is readable; inspect the wrapped cause for the real error
  2. Retry the scan to rule out transient object-store failures
  3. Ensure snapshot/manifest retention policies keep all manifests in the scanned range for the duration of the pipeline

Example fix

// before
table.expireSnapshots().expireOlderThan(System.currentTimeMillis()).commit(); // deletes manifests scan still needs
// after
long watermark = System.currentTimeMillis() - Duration.ofHours(24).toMillis();
table.expireSnapshots().expireOlderThan(watermark).commit();
Defensive patterns

Strategy: retry

Validate before calling

checkState(table.io().newInputFile(manifest.path()).exists(),
    "delete manifest missing: " + manifest.path());

Try / catch

try {
  readDeleteFiles(manifest);
} catch (RuntimeException e) {
  if (e.getMessage().startsWith("Failed to read delete manifest:")) {
    // retry with backoff; surface e.getCause() for storage diagnostics
  } else throw e;
}

Prevention

When it happens

Trigger: Reading a manifest of type DELETES during changelog task creation when the manifest is unreadable, was deleted by retention, or its entries fail to deserialize.

Common situations: Manifest files removed by expireSnapshots mid-scan; corrupted manifests; transient GCS/S3 errors; permission changes on the metadata directory.

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/beam@12126d8942 (2026-09-13). Data as JSON: /api/errors/a88300ce60c75a6c. Report an issue: GitHub.

Appendix: source

Thrown at sdks/java/io/iceberg/src/main/java/org/apache/iceberg/BeamBaseIncrementalChangelogScan.java:761

        ManifestFiles.readDeleteManifest(manifest, table().io(), table().specs())) {
      for (ManifestEntry<DeleteFile> entry : reader.entries()) {
        if (entry.status() == ManifestEntry.Status.DELETED
            && entry.snapshotId().equals(targetSnapshotId)) {
          DeleteFile file = entry.file();

          if (!partitionMatchesFilter(file)) {
            continue;
          }

          Set<Integer> columns =
              file.content() == FileContent.POSITION_DELETES
                  ? Set.of(MetadataColumns.DELETE_FILE_PATH.fieldId())
                  : Set.copyOf(file.equalityFieldIds());
          deleteFiles.add(ContentFileUtil.copy(file, true, columns));
        }
      }
    } catch (Exception e) {
      throw new RuntimeException("Failed to read delete manifest: " + manifest.path(), e);
    }

    return deleteFiles;
  }

  /**
   * Prunes delete manifests based on partition filter to avoid processing irrelevant manifests.
   * This significantly improves performance when only a subset of partitions are relevant to the
   * scan.
   *
   * @param manifests all delete manifests to consider
   * @return list of manifests that might contain relevant delete files
   */
  private List<ManifestFile> pruneManifestsByPartition(List<ManifestFile> manifests) {
    Expression currentFilter = filter();

    // If there's no filter, return all manifests
    if (currentFilter == null || currentFilter.equals(Expressions.alwaysTrue())) {

View on GitHub (pinned to 12126d8942)