{"record":{"id":"7d7bf150bb4ffc18","repo":"apache/iceberg","slug":"failed-to-delete-file-7d7bf1","errorCode":null,"errorMessage":"Failed to delete file: {}","messagePattern":"Failed to delete file: (.+?)","errorType":"console","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"spark/v4.2/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.2/spark/src/main/java/org/apache/iceberg/spark/actions/DeleteOrphanFilesSparkAction.java#L318-L354","documentation":"A logged warning emitted per-file by DeleteOrphanFilesSparkAction.deleteNonBulk when the table's FileIO does not implement SupportsBulkOperations and individual deleteFile calls fail. Tasks.foreach with suppressFailureWhenFinished and noRetry collects each failure in the onFailure handler so one bad file does not stop the sweep; failed files are simply left behind.","triggerScenarios":"Running DeleteOrphanFiles on a FileIO without bulk operations (e.g., HadoopFileIO or a custom IO) when table.io().deleteFile(file) throws per file — HDFS permission errors, files already removed by a concurrent job, or transient NameNode/storage errors.","commonSituations":"HDFS clusters where the job user lacks delete permission on the table directory; orphan sweep racing with ExpireSnapshots; files deleted between listing and delete (ENOENT); misconfigured custom FileIO.","solutions":["Re-run deleteOrphanFiles after fixing the cause; previously failed files are retried.","Grant the job's principal delete permission on the table/data directory (HDFS ACLs, POSIX perms).","Check each chained exception: 'file does not exist' means a concurrent job already deleted it (benign).","Switch to a FileIO with bulk support (e.g., S3FileIO) to reduce partial-failure windows.","Serialize orphan sweeps with other maintenance jobs on the same table."],"exampleFix":"// before\n// HadoopFileIO user lacks delete perms -> per-file warnings\nhdfs dfs -chmod -R o-rwx /warehouse/db/table  # wrong direction\n// after\nhdfs dfs -chmod -R 775 /warehouse/db/table\nhdfs dfs -chown -R hive:hive /warehouse/db/table\n// then re-run deleteOrphanFiles","handlingStrategy":"retry","validationCode":"// pre-verify delete permission on the data directory\nFileSystem fs = new Path(tableLocation).getFileSystem(conf);\nFsPermission perm = fs.getFileStatus(new Path(tableLocation)).getPermission();","typeGuard":null,"tryCatchPattern":"try { deleteNonBulk(paths); } catch (Exception e) { /* per-file failures already logged; re-run */ }","preventionTips":["Correct HDFS ACLs/ownership for the maintenance user","Prefer FileIO implementations with bulk operations","Serialize orphan sweeps with ExpireSnapshots","Ignore ENOENT causes as benign double-deletes"],"tags":["spark","actions","file-deletion","hadoop"],"backgroundTag":"file-write-failed","analyzedSha":"86d9c8fc543e7c56c9f624eb725f76c9baff9570","analyzedAt":"2026-09-12T00:46:39.097Z","contentChangedAt":"2026-09-12T00:46:39.097Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}