{"record":{"id":"0ed8ff02a60b20b7","repo":"apache/hadoop","slug":"cannot-delete-root-path-0ed8ff","errorCode":null,"errorMessage":"Cannot delete root path","messagePattern":"Cannot delete root path","errorType":"exception","errorClass":"PathIOException","httpStatus":null,"severity":"error","filePath":"hadoop-cloud-storage-project/hadoop-tos/src/main/java/org/apache/hadoop/fs/tosfs/RawFileSystem.java","lineNumber":381,"sourceCode":"   * @return true if root directory is empty, false if trying to delete a non-empty dir recursively.\n   * @throws IOException if trying to delete the non-empty root dir non-recursively.\n   */\n  private boolean deleteRoot(Path root, boolean recursive) throws IOException {\n    LOG.info(\"Delete the {} root directory of {}\", bucket, recursive);\n    boolean isEmptyDir = fsOps.isEmptyDirectory(root);\n    if (isEmptyDir) {\n      return true;\n    }\n    if (recursive) {\n      // AbstractContractRootDirectoryTest#testRmRootRecursive doesn't expect any exception if\n      // trying to delete a non-empty root directory recursively, so we have to return false here\n      // instead of throwing a IOException.\n      return false;\n    } else {\n      // AbstractContractRootDirectoryTest#testRmNonEmptyRootDirNonRecursive expect a exception if\n      // trying to delete a non-empty root directory non-recursively, so we have to throw a\n      // IOException instead of returning false.\n      throw new PathIOException(bucket, \"Cannot delete root path\");\n    }\n  }\n\n  @Override\n  public RawFileStatus[] listStatus(Path f) throws IOException {\n    LOG.debug(\"List status for path: {}\", f);\n    return Iterators.toArray(listStatus(f, false), RawFileStatus.class);\n  }\n\n  public Iterator<RawFileStatus> listStatus(Path f, boolean recursive) throws IOException {\n    Path path = makeQualified(f);\n    // Assuming path is a dir at first.\n    Iterator<RawFileStatus> iterator = fsOps.listDir(path, recursive, key -> true).iterator();\n    if (iterator.hasNext()) {\n      return iterator;\n    } else {\n      RawFileStatus fileStatus = innerFileStatus(path);\n      if (fileStatus.isFile()) {","sourceCodeStart":363,"sourceCodeEnd":399,"githubUrl":"https://github.com/apache/hadoop/blob/2add9630210752f88ceb1bb74eb65e37bf41da8e/hadoop-cloud-storage-project/hadoop-tos/src/main/java/org/apache/hadoop/fs/tosfs/RawFileSystem.java#L363-L399","documentation":"delete() on the bucket root throws PathIOException(\"Cannot delete root path\") when a non-recursive delete is attempted on a non-empty root. This is deliberate: contract tests (AbstractContractRootDirectoryTest#testRmNonEmptyRootDirNonRecursive) require an exception here, while recursive delete of a non-empty root returns false (testRmRootRecursive) and an empty root returns true.","triggerScenarios":"fs.delete(new Path(\"/\"), false) (recursive=false) when the bucket contains any objects; equivalently fs.delete(path-to-bucket-root, false) after qualification.","commonSituations":"Cleanup code that walks to the filesystem root and deletes it; tools that call delete(path, false) on directories expecting POSIX rmdir-of-empty behavior hitting a non-empty bucket; miscomputed paths that collapse to '/'.","solutions":["Never delete the root; iterate fs.listStatus(root) and delete children explicitly","If using delete(f, false) for empty-dir semantics, guard with a root check and skip or log instead","Catch PathIOException for this specific case when generic cleanup code must tolerate it"],"exampleFix":"// before\nfs.delete(new Path(\"/\"), false); // non-empty bucket root -> PathIOException\n\n// after\nPath root = fs.getHomeDirectory().getParent(); // bucket root\nif (!f.isRoot()) { fs.delete(f, recursive); }\nelse { for (FileStatus st : fs.listStatus(f)) { fs.delete(st.getPath(), true); } }","handlingStrategy":"try-catch","validationCode":"Path q = f.makeQualified(fs.getUri(), fs.getWorkingDirectory());\nboolean isRoot = q.toUri().getPath().equals(\"/\");\nif (isRoot && !recursive) {\n  // skip or list-and-delete children instead\n}","typeGuard":null,"tryCatchPattern":"try {\n  fs.delete(f, false);\n} catch (PathIOException e) {\n  if (\"Cannot delete root path\".equals(e.getOperation())) { /* root guard hit: clean children instead */ }\n  else throw e;\n}","preventionTips":["Never issue delete() on the qualified root path","Guard cleanup loops with isRoot checks","Remember contract semantics: recursive root delete returns false, non-recursive throws"],"tags":["delete","root-directory","pathioexception","contract-semantics","tos"],"backgroundTag":"root-directory-protected","analyzedSha":"2add9630210752f88ceb1bb74eb65e37bf41da8e","analyzedAt":"2026-08-22T19:55:07.957Z","schemaVersion":2},"datasetVersion":"2026-08-22T20:17:22.307Z"}