apache/iceberg · warning

Bulk deletion failed

Error message

Bulk deletion failed

What it means

FileCleanupStrategy.deleteFiles first attempts a bulk delete API (e.g. S3 deleteObjects) via Tasks; if that bulk call fails outright it logs 'Bulk deletion failed' at WARN. The failure is logged and swallowed so the cleanup/expire operation itself does not abort; un-deleted files may be left behind as orphans.

Source

Thrown at core/src/main/java/org/apache/iceberg/FileCleanupStrategy.java:119

          .build();
    } else {
      return CloseableIterable.withNoopClose(snapshot.allManifests(fileIO));
    }
  }

  protected void deleteFiles(Set<String> pathsToDelete, String fileType) {
    if (deleteFunc == null && fileIO instanceof SupportsBulkOperations) {
      try {
        ((SupportsBulkOperations) fileIO).deleteFiles(pathsToDelete);
      } catch (BulkDeletionFailureException e) {
        LOG.warn(
            "Bulk deletion failed for {} of {} {} file(s)",
            e.numberFailedObjects(),
            pathsToDelete.size(),
            fileType,
            e);
      } catch (RuntimeException e) {
        LOG.warn("Bulk deletion failed", e);
      }
    } else {
      Consumer<String> deleteFuncToUse = deleteFunc == null ? defaultDeleteFunc : deleteFunc;

      Tasks.foreach(pathsToDelete)
          .executeWith(deleteExecutorService)
          .retry(3)
          .stopRetryOn(NotFoundException.class)
          .stopOnFailure()
          .suppressFailureWhenFinished()
          .onFailure(
              (file, thrown) -> LOG.warn("Delete failed for {} file: {}", fileType, file, thrown))
          .run(deleteFuncToUse::accept);
    }
  }

  protected boolean hasAnyStatisticsFiles(TableMetadata tableMetadata) {
    return !tableMetadata.statisticsFiles().isEmpty()

View on GitHub (pinned to 86d9c8fc54)

Solutions

  1. Check the logged cause 'e' in the WARN stack trace to identify the underlying storage error (permissions, throttling, unsupported operation).
  2. Ensure the FileIO credentials/permissions allow bulk deletion of the given file type (manifests, data, manifests-lists).
  3. If the store throttles, reduce delete concurrency or batch size, or retry the expire operation — remaining files can be cleaned by a later run.
  4. If the FileIO does not support bulk delete, implement bulkDelete() or configure a FileIO that does, so the non-bulk fallback path is not needed.

Example fix

// before: custom FileIO without bulk delete support
public void deleteFiles(Iterable<String> paths) { throw new UnsupportedOperationException(); }
// after: implement bulk delete or return unimplemented so the fallback single-delete path is used
public void deleteFiles(Iterable<String> pathsToRemove) {
  Tasks.foreach(pathsToRemove).suppressFailureWhenFinished().run(io::deleteFile);
}
Defensive patterns

Strategy: retry

Validate before calling

// before running cleanup, verify delete permission on one sample path
io.deleteFile(samplePath); // throws early if credentials are insufficient

Try / catch

try {
  table.expireSnapshots().expireOlderThan(ts).execute();
} catch (RuntimeException e) {
  LOG.warn("Expiration reported partial failures; will re-run for orphans", e);
}

Prevention

When it happens

Trigger: Table maintenance (expireSnapshots, deleteFiles/orphan cleanup) calls deleteFiles with enough paths to take the bulk-deletion path, and the bulk delete call throws (IOException, rate limiting, ObjectStore permission error, bulk-delete not supported by the FileIO). Also hit when a custom FileIO's bulk delete implementation is not implemented and throws RuntimeException.

Common situations: S3/GCS throttling or 503s during heavy expiration of many snapshots; IAM credentials lacking s3:DeleteObjects; a custom FileIO whose bulkDelete() throws UnsupportedOperationException.

Related errors


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