{"record":{"id":"3ea6054c7573ec74","repo":"apache/iceberg","slug":"failed-to-delete-file-3ea605","errorCode":null,"errorMessage":"Failed to delete file: {}","messagePattern":"Failed to delete file: (.+?)","errorType":"console","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"spark/v4.1/spark/src/main/java/org/apache/iceberg/spark/actions/DeleteOrphanFilesSparkAction.java","lineNumber":336,"sourceCode":"\n  private void deleteBulk(SupportsBulkOperations io, List<String> paths) {\n    try {\n      io.deleteFiles(paths);\n      LOG.info(\"Deleted {} files using bulk deletes\", paths.size());\n    } catch (BulkDeletionFailureException e) {\n      int deletedFilesCount = paths.size() - e.numberFailedObjects();\n      LOG.warn(\n          \"Deleted only {} of {} files using bulk deletes\", deletedFilesCount, paths.size(), e);\n    }\n  }\n\n  private void deleteNonBulk(List<String> paths) {\n    Tasks.Builder<String> deleteTasks =\n        Tasks.foreach(paths)\n            .noRetry()\n            .executeWith(deleteExecutorService)\n            .suppressFailureWhenFinished()\n            .onFailure((file, exc) -> LOG.warn(\"Failed to delete file: {}\", file, exc));\n\n    if (deleteFunc == null) {\n      LOG.info(\n          \"Table IO {} does not support bulk operations. Using non-bulk deletes.\",\n          table.io().getClass().getName());\n      deleteTasks.run(table.io()::deleteFile);\n    } else {\n      LOG.info(\"Custom delete function provided. Using non-bulk deletes\");\n      deleteTasks.run(deleteFunc::accept);\n    }\n  }\n\n  @VisibleForTesting\n  static Dataset<String> findOrphanFiles(\n      Dataset<FileURI> actualFileIdentDS,\n      Dataset<FileURI> validFileIdentDS,\n      PrefixMismatchMode prefixMismatchMode) {\n","sourceCodeStart":318,"sourceCodeEnd":354,"githubUrl":"https://github.com/apache/iceberg/blob/86d9c8fc543e7c56c9f624eb725f76c9baff9570/spark/v4.1/spark/src/main/java/org/apache/iceberg/spark/actions/DeleteOrphanFilesSparkAction.java#L318-L354","documentation":"DeleteOrphanFilesSparkAction.deleteNonBulk is the fallback for FileIO implementations without bulk support; each file is deleted in parallel via Tasks with noRetry and failure suppression, and per-file failures are logged as 'Failed to delete file: <path>'. The action completes even if some deletes failed, so files may remain.","triggerScenarios":"Any individual table.io().deleteFile(path) (or custom deleteFunc) throwing during a non-bulk orphan-file cleanup — permissions, transient storage errors, or file already removed by another process.","commonSituations":"Using HadoopFileIO or custom IO without SupportsBulkOperations; concurrent ExpireSnapshots and RemoveOrphanFiles on the same table; read-only mount or missing credentials.","solutions":["Review the logged exceptions per file and fix the storage-level cause (credentials, permissions)","Re-run the RemoveOrphanFiles action after fixing to delete the leftover files","Use a FileIO with bulk support (e.g. S3FileIO) for more efficient and resumable deletes"],"exampleFix":null,"handlingStrategy":"retry","validationCode":null,"typeGuard":null,"tryCatchPattern":"try {\n  Actions.forTable(table).deleteOrphanFiles().execute();\n} catch (Exception e) {\n  // per-file failures were logged; fix and re-run\n}","preventionTips":["Prefer a FileIO implementing SupportsBulkOperations","Check write/delete permissions before large cleanups","Stagger maintenance jobs to avoid races"],"tags":["spark","file-deletion","orphan-files","object-store"],"backgroundTag":"http-request-failed","analyzedSha":"86d9c8fc543e7c56c9f624eb725f76c9baff9570","analyzedAt":"2026-09-12T00:46:39.097Z","contentChangedAt":"2026-09-12T00:46:39.097Z","schemaVersion":2},"datasetVersion":"2026-09-14T16:17:12.679Z"}