apache/iceberg · warning

Failed to get deleted files: this may cause orphaned data fi

Error message

Failed to get deleted files: this may cause orphaned data files

What it means

During incremental cleanup, findFilesToDelete scans manifests with DELETE entries in parallel to compute deleted data files. If scanning a manifest fails (after 3 retries), this WARN 'Failed to get deleted files: this may cause orphaned data files' is logged. Because the failed manifest's DELETE entries are unknown, files that should be expired may be left as orphans.

Source

Thrown at core/src/main/java/org/apache/iceberg/IncrementalFileCleanup.java:294

          expiredStatisticsFilesLocations(beforeExpiration, afterExpiration);
      LOG.debug("Deleting {} statistics files", expiredStatisticsFilesLocations.size());
      deleteFiles(expiredStatisticsFilesLocations, "statistics files");
    }
  }

  private Set<String> findFilesToDelete(
      Set<ManifestFile> manifestsToScan,
      Set<ManifestFile> manifestsToRevert,
      Set<Long> validIds,
      Map<Integer, PartitionSpec> specsById) {
    Set<String> filesToDelete = ConcurrentHashMap.newKeySet();
    Tasks.foreach(manifestsToScan)
        .retry(3)
        .suppressFailureWhenFinished()
        .executeWith(planExecutorService)
        .onFailure(
            (item, exc) ->
                LOG.warn("Failed to get deleted files: this may cause orphaned data files", exc))
        .run(
            manifest -> {
              // the manifest has deletes, scan it to find files to delete
              try (ManifestReader<?> reader = ManifestFiles.open(manifest, fileIO, specsById)) {
                for (ManifestEntry<?> entry : reader.entries()) {
                  // if the snapshot ID of the DELETE entry is no longer valid, the data can be
                  // deleted
                  if (entry.status() == ManifestEntry.Status.DELETED
                      && !validIds.contains(entry.snapshotId())) {
                    // use toString to ensure the path will not change (Utf8 is reused)
                    filesToDelete.add(entry.file().location());
                  }
                }
              } catch (IOException e) {
                throw new RuntimeIOException(e, "Failed to read manifest file: %s", manifest);
              }
            });

View on GitHub (pinned to 86d9c8fc54)

Solutions

  1. Check the WARN cause and verify the manifest file exists and is readable via the FileIO.
  2. Do not run concurrent expirations; rerun expiration later so missed manifests are scanned then.
  3. Plan a full (non-incremental) cleanup or an orphan-file removal to catch files skipped by this run.
  4. Increase retry robustness (check network/config) if throttling is the cause.
Defensive patterns

Strategy: retry

Validate before calling

// verify manifests referenced by snapshots are readable before cleanup
ValidationHelpers.checkManifestsReadable(table, io); // custom check opening each manifest

Try / catch

try {
  table.expireSnapshots().execute();
} catch (RuntimeException e) {
  LOG.warn("Some manifests could not be scanned; orphan sweep recommended", e);
}

Prevention

When it happens

Trigger: filesToDelete -> findFilesToDelete opens a ManifestReader (ManifestFiles.open) for each manifest to scan and iterates entries; a manifest object is unreadable/corrupted, or the IO throws transiently, for manifests flagged as having deletes.

Common situations: Manifests deleted or unreadable due to eventual consistency after concurrent expiration; corrupt manifests from a failed write; missing object-store read permissions; throttling under parallel planning.

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