{"record":{"id":"d5db0c665d3a44e3","repo":"apache/hadoop","slug":"cannot-move-path-to-the-trash-as-it-contains","errorCode":null,"errorMessage":"Cannot move \"{path}\" to the trash, as it contains the trash","messagePattern":"Cannot move \"(.+?)\" to the trash, as it contains the trash","errorType":"exception","errorClass":"IOException","httpStatus":null,"severity":"error","filePath":"hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/fs/TrashPolicyDefault.java","lineNumber":149,"sourceCode":"  public boolean moveToTrash(Path path) throws IOException {\n    if (!isEnabled())\n      return false;\n\n    if (!path.isAbsolute())                       // make path absolute\n      path = new Path(fs.getWorkingDirectory(), path);\n\n    // check that path exists\n    fs.getFileStatus(path);\n    String qpath = fs.makeQualified(path).toString();\n\n    Path trashRoot = fs.getTrashRoot(path);\n    Path trashCurrent = new Path(trashRoot, CURRENT);\n    if (qpath.startsWith(trashRoot.toString())) {\n      return false;                               // already in trash\n    }\n\n    if (trashRoot.getParent().toString().startsWith(qpath)) {\n      throw new IOException(\"Cannot move \\\"\" + path +\n                            \"\\\" to the trash, as it contains the trash\");\n    }\n\n    Path trashPath = makeTrashRelativePath(trashCurrent, path);\n    Path baseTrashPath = makeTrashRelativePath(trashCurrent, path.getParent());\n    \n    IOException cause = null;\n\n    // try twice, in case checkpoint between the mkdirs() & rename()\n    for (int i = 0; i < 2; i++) {\n      try {\n        if (!fs.mkdirs(baseTrashPath, PERMISSION)) {      // create current\n          LOG.warn(\"Can't create(mkdir) trash directory: \" + baseTrashPath);\n          return false;\n        }\n      } catch (FileAlreadyExistsException | ParentNotDirectoryException e) {\n        // find the path which is not a directory, and modify baseTrashPath\n        // & trashPath, then mkdirs","sourceCodeStart":131,"sourceCodeEnd":167,"githubUrl":"https://github.com/apache/hadoop/blob/2add9630210752f88ceb1bb74eb65e37bf41da8e/hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/fs/TrashPolicyDefault.java#L131-L167","documentation":"Thrown by TrashPolicyDefault.moveToTrash when the path being deleted is an ancestor of (or equal to) the trash root - i.e. deleting it would delete the trash itself. After verifying the path exists and is not already inside the trash, the policy checks trashRoot.getParent().toString().startsWith(qpath) and refuses with IOException. It prevents rm -r from destroying the safety net it is supposed to use.","triggerScenarios":"'hadoop fs -rm -r /user' or 'rm -r /user/alice' when the trash root is /user/alice/.Trash; any delete of a high-level directory (home root, /user, /) that contains the .Trash directory of the current user.","commonSituations":"Admin cleanup scripts running rm -r on home directories; recursive deletes that walk up too far; users surprised that deleting their own home directory is blocked.","solutions":["Delete the specific subpaths that do not contain the trash, e.g. rm -r /user/alice/data instead of /user/alice","If you truly intend to remove everything including trash: run 'hadoop fs -expunge' first, or delete with -skipTrash","Guard admin scripts by refusing to rm -r any path that is a prefix of the user's trash root"],"exampleFix":"// before\nfs.delete(new Path(\"/user/alice\"), true); // contains /user/alice/.Trash\n\n// after\nPath trashRoot = fs.getTrashRoot(new Path(\"/user/alice\"));\n// delete children, excluding anything at or above the trash root\nfor (FileStatus st : fs.listStatus(new Path(\"/user/alice\"))) {\n  if (!trashRoot.toString().startsWith(st.getPath().toString())) {\n    fs.delete(st.getPath(), true);\n  }\n}","handlingStrategy":"validation","validationCode":"Path trashRoot = fs.getTrashRoot(path);\nString qpath = fs.makeQualified(path).toString();\nif (trashRoot.getParent() != null\n    && trashRoot.getParent().toString().startsWith(qpath)) {\n  // would delete the trash itself: refuse or enumerate children instead\n  throw new IOException(\"Refusing to delete ancestor of trash: \" + path);\n}\nfs.delete(path, true);","typeGuard":null,"tryCatchPattern":"try {\n  fs.delete(path, true);\n} catch (IOException e) {\n  if (e.getMessage() != null && e.getMessage().contains(\"contains the trash\")) {\n    // delete children individually, or use -skipTrash deliberately\n  }\n}","preventionTips":["Never rm -r a user's home root or /user when trash is enabled; target concrete subdirectories","In admin scripts, compare the delete path against getTrashRoot() and refuse prefixes"],"tags":["hadoop","trash","delete","path-conflict"],"backgroundTag":"trash-root-conflict","analyzedSha":"2add9630210752f88ceb1bb74eb65e37bf41da8e","analyzedAt":"2026-08-22T19:55:07.957Z","schemaVersion":2},"datasetVersion":"2026-08-22T20:17:22.307Z"}