{"record":{"id":"95a06f2e76cbc72f","repo":"apache/hadoop","slug":"path","errorCode":null,"errorMessage":"{path}","messagePattern":"\\{path\\}","errorType":"exception","errorClass":"PathIsNotEmptyDirectoryException","httpStatus":null,"severity":"error","filePath":"hadoop-cloud-storage-project/hadoop-huaweicloud/src/main/java/org/apache/hadoop/fs/obs/OBSObjectBucketUtils.java","lineNumber":260,"sourceCode":"    String key = OBSCommonUtils.pathToKey(owner, f);\n\n    if (status.isDirectory()) {\n      LOG.debug(\"delete: Path is a directory: {} - recursive {}\", f,\n          recursive);\n\n      key = OBSCommonUtils.maybeAddTrailingSlash(key);\n      if (!key.endsWith(\"/\")) {\n        key = key + \"/\";\n      }\n\n      boolean isEmptyDir = OBSCommonUtils.isFolderEmpty(owner, key);\n      if (key.equals(\"/\")) {\n        return OBSCommonUtils.rejectRootDirectoryDelete(\n            owner.getBucket(), isEmptyDir, recursive);\n      }\n\n      if (!recursive && !isEmptyDir) {\n        throw new PathIsNotEmptyDirectoryException(f.toString());\n      }\n\n      if (isEmptyDir) {\n        LOG.debug(\n            \"delete: Deleting fake empty directory {} - recursive {}\",\n            f, recursive);\n        OBSCommonUtils.deleteObject(owner, key);\n      } else {\n        LOG.debug(\n            \"delete: Deleting objects for directory prefix {} \"\n                + \"- recursive {}\",\n            f, recursive);\n        deleteNonEmptyDir(owner, recursive, key);\n      }\n\n    } else {\n      LOG.debug(\"delete: Path is a file\");\n      OBSCommonUtils.deleteObject(owner, key);","sourceCodeStart":242,"sourceCodeEnd":278,"githubUrl":"https://github.com/apache/hadoop/blob/2add9630210752f88ceb1bb74eb65e37bf41da8e/hadoop-cloud-storage-project/hadoop-huaweicloud/src/main/java/org/apache/hadoop/fs/obs/OBSObjectBucketUtils.java#L242-L278","documentation":"For directory deletes, the code checks whether the directory prefix has any contents; delete(f, recursive=false) on a non-empty directory throws Hadoop's PathIsNotEmptyDirectoryException with the path string as the message. The bucket root is refused separately by rejectRootDirectoryDelete, and an empty directory marker is deleted as a single object.","triggerScenarios":"fs.delete(dir, false) when dir contains objects or subdirectory markers; cleanup code written for a POSIX filesystem where false was thought sufficient; dropping a partition directory without the recursive flag.","commonSituations":"Porting local-filesystem utilities to object storage where 'directory' means a prefix with keys; a race where another writer adds an object between the emptiness check and the delete call; Hive/Spark partition drop configured non-recursive.","solutions":["Pass recursive=true when the whole tree should be removed: fs.delete(dir, true)","List and delete children explicitly when only some entries should go","On a suspected race, catch the exception, re-list, and retry once"],"exampleFix":"// before\nfs.delete(dir, false);\n\n// after\nfs.delete(dir, true);","handlingStrategy":"validation","validationCode":"if (fs.getFileStatus(dir).isDirectory()) {\n  boolean empty = !fs.listStatus(dir).iterator().hasNext()\n      && fs.listStatus(dir).length == 0;\n  if (!empty) {\n    // pass recursive=true, or delete children selectively\n  }\n}","typeGuard":"static boolean isEmptyDirectory(FileSystem fs, Path p) throws IOException {\n  return fs.getFileStatus(p).isDirectory() && fs.listStatus(p).length == 0;\n}","tryCatchPattern":"try {\n  fs.delete(dir, false);\n} catch (PathIsNotEmptyDirectoryException e) {\n  // content appeared between check and delete: list again and decide,\n  // or escalate to fs.delete(dir, true) if full removal is intended\n}","preventionTips":["Default to recursive=true when the whole tree should go","Use listStatus-based selective deletes for partial cleanup","Re-check emptiness right before non-recursive deletes in concurrent environments"],"tags":["hadoop","obs","delete","directory-not-empty","recursive"],"backgroundTag":"directory-not-empty","analyzedSha":"2add9630210752f88ceb1bb74eb65e37bf41da8e","analyzedAt":"2026-08-22T19:55:07.957Z","schemaVersion":2},"datasetVersion":"2026-08-22T20:17:22.307Z"}