apache/iceberg · warning

Delete failed for {}: {}

Error message

Delete failed for {}: {}

What it means

BaseSparkAction.deleteFiles runs file deletions in parallel via Tasks with failure suppression; when an individual delete fails, the onFailure handler logs 'Delete failed for <type>: <path>' with the exception and deletion continues for other files. The overall action may finish even though some files were not deleted, so orphan/obsolete files can remain.

Source

Thrown at spark/v4.1/spark/src/main/java/org/apache/iceberg/spark/actions/BaseSparkAction.java:255

   * @param deleteFunc a delete func
   * @param files an iterator of Spark rows of the structure (path: String, type: String)
   * @return stats on which files were deleted
   */
  protected DeleteSummary deleteFiles(
      ExecutorService executorService, Consumer<String> deleteFunc, Iterator<FileInfo> files) {

    DeleteSummary summary = new DeleteSummary();

    Tasks.foreach(files)
        .retry(DELETE_NUM_RETRIES)
        .stopRetryOn(NotFoundException.class)
        .suppressFailureWhenFinished()
        .executeWith(executorService)
        .onFailure(
            (fileInfo, exc) -> {
              String path = fileInfo.getPath();
              String type = fileInfo.getType();
              LOG.warn("Delete failed for {}: {}", type, path, exc);
            })
        .run(
            fileInfo -> {
              String path = fileInfo.getPath();
              String type = fileInfo.getType();
              deleteFunc.accept(path);
              summary.deletedFile(path, type);
            });

    return summary;
  }

  protected DeleteSummary deleteFiles(SupportsBulkOperations io, Iterator<FileInfo> files) {
    DeleteSummary summary = new DeleteSummary();
    Iterator<List<FileInfo>> fileGroups = Iterators.partition(files, DELETE_GROUP_SIZE);

    Tasks.foreach(fileGroups)
        .suppressFailureWhenFinished()

View on GitHub (pinned to 86d9c8fc54)

Solutions

  1. Inspect each logged path/exception to determine whether failures are transient and re-run the action
  2. Avoid concurrent delete actions on the same table (they race to delete the same files)
  3. Add retry/backoff or reduce delete concurrency if the storage layer is throttling
Defensive patterns

Strategy: retry

Try / catch

try {
  Actions.forTable(table).rewriteDataFiles().execute();
} catch (Exception e) {
  // check logs for 'Delete failed for <type>: <path>' entries and re-run
}

Prevention

When it happens

Trigger: deleteFunc (e.g. io.deleteFile) throws for a specific file during rewrite/expire actions — storage permissions issues, S3 throttling, or the file already being deleted by another process.

Common situations: Concurrent ExpireSnapshots/DeleteOrphanFiles runs racing each other; bucketing/permissions changes mid-run; transient S3 503s under heavy delete load.

Understand the failure class

Background: 'Something went wrong' / 'Request failed (500)' / 'HTTP error! status: 404' — what failed HTTP requests actually mean and how to find the real cause — this error's family across 28 libraries.

Related errors


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