apache/iceberg · warning

Deleted only {} of {} files using bulk deletes

Error message

Deleted only {} of {} files using bulk deletes

What it means

This is a WARN log, not a thrown exception, emitted when a bulk delete against a SupportsBulkOperations FileIO partially fails. The FileIO raised BulkDeletionFailureException carrying numberFailedObjects(); the action computes how many paths were actually deleted and logs the shortfall along with the exception for the stack trace.

Source

Thrown at spark/v4.0/spark/src/main/java/org/apache/iceberg/spark/actions/DeleteOrphanFilesSparkAction.java:325

  }

  private void collectPathsForOutput(
      List<String> paths, List<String> orphanFileList, int maxSampleSize) {
    if (streamResults()) {
      int lengthToAdd = Math.min(maxSampleSize - orphanFileList.size(), paths.size());
      orphanFileList.addAll(paths.subList(0, lengthToAdd));
    } else {
      orphanFileList.addAll(paths);
    }
  }

  private void deleteBulk(SupportsBulkOperations io, List<String> paths) {
    try {
      io.deleteFiles(paths);
      LOG.info("Deleted {} files using bulk deletes", paths.size());
    } catch (BulkDeletionFailureException e) {
      int deletedFilesCount = paths.size() - e.numberFailedObjects();
      LOG.warn(
          "Deleted only {} of {} files using bulk deletes", deletedFilesCount, paths.size(), e);
    }
  }

  private void deleteNonBulk(List<String> paths) {
    Tasks.Builder<String> deleteTasks =
        Tasks.foreach(paths)
            .noRetry()
            .executeWith(deleteExecutorService)
            .suppressFailureWhenFinished()
            .onFailure((file, exc) -> LOG.warn("Failed to delete file: {}", file, exc));

    if (deleteFunc == null) {
      LOG.info(
          "Table IO {} does not support bulk operations. Using non-bulk deletes.",
          table.io().getClass().getName());
      deleteTasks.run(table.io()::deleteFile);
    } else {

View on GitHub (pinned to 86d9c8fc54)

Solutions

  1. Inspect the attached exception stack for the underlying per-object delete errors and fix those (permissions, lifecycle conflicts).
  2. Re-run the RemoveOrphanFiles action; the remaining undeleted files are retried and the operation is idempotent.
  3. If failures are because files were concurrently deleted, ignore the warning — partial success is expected in that race.
  4. If the FileIO's bulk-delete endpoint is flaky, force the non-bulk path by using a FileIO that does not implement SupportsBulkOperations or a custom deleteFunc via deleteWith.

Example fix

// before: assuming all files deleted
io.deleteFiles(paths);
// after: treat partial failure as recoverable
try {
  io.deleteFiles(paths);
} catch (BulkDeletionFailureException e) {
  List<String> retry = ...; // re-run deleteFile on failed paths individually
}
Defensive patterns

Strategy: retry

Validate before calling

boolean bulk = table.io() instanceof org.apache.iceberg.io.SupportsBulkOperations;

Type guard

if (table.io() instanceof SupportsBulkOperations io) { /* bulk path */ }

Try / catch

try { io.deleteFiles(paths); } catch (BulkDeletionFailureException e) { List<String> remaining = /* paths minus e.deletedCount */; retryDeleteIndividually(remaining); }

Prevention

When it happens

Trigger: DeleteOrphanFilesSparkAction.deleteFiles dispatches to deleteBulk because table.io() implements SupportsBulkOperations, and io.deleteFiles(paths) throws BulkDeletionFailureException (e.g. S3 multipart delete returns individual object failures).

Common situations: S3ObjectStoreFileIO/HadoopFileIO bulk deletes where some objects were already gone, are write-protected, or hit transient S3 errors; stale orphan-file paths that another concurrent expiration already removed.

Related errors


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