{"record":{"id":"c83c1051cfeccc87","repo":"apache/hadoop","slug":"cannot-delete-rename-non-empty-protected-directory","errorCode":null,"errorMessage":"Cannot delete/rename non-empty protected directory {}","messagePattern":"Cannot delete/rename non-empty protected directory (.+?)","errorType":"exception","errorClass":"AccessControlException","httpStatus":null,"severity":"error","filePath":"hadoop-hdfs-project/hadoop-hdfs/src/main/java/org/apache/hadoop/hdfs/DFSUtil.java","lineNumber":1896,"sourceCode":"   * @param iip directory whose descendants are to be checked.\n   * @throws AccessControlException if a non-empty protected descendant\n   *                                was found.\n   * @throws ParentNotDirectoryException\n   * @throws UnresolvedLinkException\n   */\n  public static void checkProtectedDescendants(\n      FSDirectory fsd, INodesInPath iip)\n          throws AccessControlException, UnresolvedLinkException,\n          ParentNotDirectoryException {\n    final SortedSet<String> protectedDirs = fsd.getProtectedDirectories();\n    if (protectedDirs.isEmpty()) {\n      return;\n    }\n\n    String src = iip.getPath();\n    // Is src protected? Caller has already checked it is non-empty.\n    if (protectedDirs.contains(src)) {\n      throw new AccessControlException(\n          \"Cannot delete/rename non-empty protected directory \" + src);\n    }\n\n    // Are any descendants of src protected?\n    // The subSet call returns only the descendants of src since\n    // {@link Path#SEPARATOR} is \"/\" and '0' is the next ASCII\n    // character after '/'.\n    for (String descendant :\n        protectedDirs.subSet(src + Path.SEPARATOR, src + \"0\")) {\n      INodesInPath subdirIIP =\n          fsd.getINodesInPath(descendant, FSDirectory.DirOp.WRITE);\n      if (fsd.isNonEmptyDirectory(subdirIIP)) {\n        throw new AccessControlException(\n            \"Cannot delete/rename non-empty protected subdirectory \"\n            + descendant);\n      }\n    }\n","sourceCodeStart":1878,"sourceCodeEnd":1914,"githubUrl":"https://github.com/apache/hadoop/blob/2add9630210752f88ceb1bb74eb65e37bf41da8e/hadoop-hdfs-project/hadoop-hdfs/src/main/java/org/apache/hadoop/hdfs/DFSUtil.java#L1878-L1914","documentation":"HDFS protected directories (fs.protected.directories, CommonConfigurationKeysPublic.java:153: 'Directories that cannot be removed unless empty, even by an administrator') are enforced by DFSUtil.checkProtectedDescendants, called on every delete (FSDirDeleteOp.java:114) and rename (FSDirRenameOp.java:287,509). If the path being deleted/renamed is itself in the protected set and non-empty, AccessControlException is thrown.","triggerScenarios":"hdfs dfs -rm -r (or rename) targeting a directory that is listed in fs.protected.directories on the NameNode and still contains children.","commonSituations":"Admins protect data zones such as /warehouse or /user/archive; batch jobs or users then attempt recursive deletes/reorganizations of those paths and are refused even with superuser rights.","solutions":["Delete or move the directory's contents first; an empty protected directory may then be removed, which is the intended escape hatch","Operate on different paths outside the protected set","If removal is genuinely intended, have the HDFS admin remove the path from fs.protected.directories in the NameNode config and restart/reload before retrying"],"exampleFix":"// before\nhdfs dfs -rm -r /warehouse          // protected and non-empty -> AccessControlException\n// after\nhdfs dfs -rm /warehouse/*           // empty it first\nhdfs dfs -rm /warehouse             // empty protected dir is now allowed","handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"try {\n  fs.delete(path, true);\n} catch (AccessControlException e) {\n  if (e.getMessage() != null && e.getMessage().contains(\"protected\")) {\n    // path is policy-protected and non-empty: skip, do not escalate\n  } else {\n    throw e;\n  }\n}","preventionTips":["Keep the current fs.protected.directories list visible to jobs (document it) so pipelines avoid protected roots","Design cleanup jobs to delete contents first and tolerate the empty-dir-only rule","Admins: normalize paths and test the protected set with a scratch directory before protecting production paths"],"tags":["hdfs","namenode","protected-directories","delete","rename","permissions"],"backgroundTag":"operation-blocked-by-policy","analyzedSha":"2add9630210752f88ceb1bb74eb65e37bf41da8e","analyzedAt":"2026-08-22T19:55:07.957Z","schemaVersion":2},"datasetVersion":"2026-08-23T01:17:44.959Z"}