{"record":{"id":"3f2e85db3d34935f","repo":"apache/iceberg","slug":"failed-to-delete-path-3f2e85","errorCode":null,"errorMessage":"Failed to delete path: {}","messagePattern":"Failed to delete path: (.+?)","errorType":"console","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"gcp/src/main/java/org/apache/iceberg/gcp/gcs/GCSFileIO.java","lineNumber":127,"sourceCode":"\n  @Override\n  public InputFile newInputFile(String path, long length) {\n    return GCSInputFile.fromLocation(path, length, clientForStoragePath(path), metrics);\n  }\n\n  @Override\n  public OutputFile newOutputFile(String path) {\n    return GCSOutputFile.fromLocation(path, clientForStoragePath(path), metrics);\n  }\n\n  @SuppressWarnings(\"resource\")\n  @Override\n  public void deleteFile(String path) {\n    // There is no specific contract about whether delete should fail\n    // and other FileIO providers ignore failure.  Log the failure for\n    // now as it is not a required operation for Iceberg.\n    if (!clientForStoragePath(path).storage().delete(BlobId.fromGsUtilUri(path))) {\n      LOG.warn(\"Failed to delete path: {}\", path);\n    }\n  }\n\n  @Override\n  public Map<String, String> properties() {\n    return properties.immutableMap();\n  }\n\n  public Storage client() {\n    return client(ROOT_STORAGE_PREFIX);\n  }\n\n  @SuppressWarnings(\"resource\")\n  public Storage client(String storagePath) {\n    return clientForStoragePath(storagePath).storage();\n  }\n\n  private PrefixedStorage clientForStoragePath(String storagePath) {","sourceCodeStart":109,"sourceCodeEnd":145,"githubUrl":"https://github.com/apache/iceberg/blob/86d9c8fc543e7c56c9f624eb725f76c9baff9570/gcp/src/main/java/org/apache/iceberg/gcp/gcs/GCSFileIO.java#L109-L145","documentation":"GCSFileIO.deleteFile logs a warning when the GCS storage client reports that the blob at the given path could not be deleted. Iceberg treats delete as best-effort here because there is no strict contract on delete failure, so no exception is thrown — the failure is only surfaced via LOG.warn. Downstream code may proceed assuming the file is gone when it is not.","triggerScenarios":"Calling GCSFileIO.deleteFile(path) when the blob does not exist, the credentials lack storage.objects.delete permission, the bucket is in a different project, or a transient GCS error makes client.storage().delete(...) return false.","commonSituations":"Over-permissive assumptions about service-account rights when expiring old data files or orphan manifests; deleted-already blobs during concurrent commit/expiry; bucket lifecycle policies racing with Iceberg deleteFile.","solutions":["Grant the service account storage.objects.delete (e.g. roles/storage.objectAdmin) on the bucket","Check the path is a valid gs:// URI pointing at an existing blob (BlobId.fromGsUtilUri)","Retry the delete; transient GCS errors can return false","Enable GCS request logging to see the underlying error code for the failed delete"],"exampleFix":"// before\nio.deleteFile(\"gs://bucket/table/data.parquet\");\n// after\nif (!blobExists(io, \"gs://bucket/table/data.parquet\")) {\n  LOG.info(\"skip delete, blob already gone\");\n} else {\n  io.deleteFile(\"gs://bucket/table/data.parquet\"); // verify in logs it succeeded\n}","handlingStrategy":"validation","validationCode":"if (path == null || !path.startsWith(\"gs://\")) throw new IllegalArgumentException(\"not a gs:// path: \" + path);","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Grant storage.objects.delete to the service account","Verify paths with BlobId.fromGsUtilUri before delete","Treat delete as best-effort; check logs for the warning","Monitor for leaked files via lifecycle rules as backstop"],"tags":["gcs","delete","fileio","best-effort"],"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"}