apache/hadoop · error · IOException

Cannot move "{path}" to the trash, as it contains the trash

Error message

Cannot move "{path}" to the trash, as it contains the trash

What it means

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.

Source

Thrown at hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/fs/TrashPolicyDefault.java:149

  public boolean moveToTrash(Path path) throws IOException {
    if (!isEnabled())
      return false;

    if (!path.isAbsolute())                       // make path absolute
      path = new Path(fs.getWorkingDirectory(), path);

    // check that path exists
    fs.getFileStatus(path);
    String qpath = fs.makeQualified(path).toString();

    Path trashRoot = fs.getTrashRoot(path);
    Path trashCurrent = new Path(trashRoot, CURRENT);
    if (qpath.startsWith(trashRoot.toString())) {
      return false;                               // already in trash
    }

    if (trashRoot.getParent().toString().startsWith(qpath)) {
      throw new IOException("Cannot move \"" + path +
                            "\" to the trash, as it contains the trash");
    }

    Path trashPath = makeTrashRelativePath(trashCurrent, path);
    Path baseTrashPath = makeTrashRelativePath(trashCurrent, path.getParent());
    
    IOException cause = null;

    // try twice, in case checkpoint between the mkdirs() & rename()
    for (int i = 0; i < 2; i++) {
      try {
        if (!fs.mkdirs(baseTrashPath, PERMISSION)) {      // create current
          LOG.warn("Can't create(mkdir) trash directory: " + baseTrashPath);
          return false;
        }
      } catch (FileAlreadyExistsException | ParentNotDirectoryException e) {
        // find the path which is not a directory, and modify baseTrashPath
        // & trashPath, then mkdirs

View on GitHub (pinned to 2add963021)

Solutions

  1. Delete the specific subpaths that do not contain the trash, e.g. rm -r /user/alice/data instead of /user/alice
  2. If you truly intend to remove everything including trash: run 'hadoop fs -expunge' first, or delete with -skipTrash
  3. Guard admin scripts by refusing to rm -r any path that is a prefix of the user's trash root

Example fix

// before
fs.delete(new Path("/user/alice"), true); // contains /user/alice/.Trash

// after
Path trashRoot = fs.getTrashRoot(new Path("/user/alice"));
// delete children, excluding anything at or above the trash root
for (FileStatus st : fs.listStatus(new Path("/user/alice"))) {
  if (!trashRoot.toString().startsWith(st.getPath().toString())) {
    fs.delete(st.getPath(), true);
  }
}
Defensive patterns

Strategy: validation

Validate before calling

Path trashRoot = fs.getTrashRoot(path);
String qpath = fs.makeQualified(path).toString();
if (trashRoot.getParent() != null
    && trashRoot.getParent().toString().startsWith(qpath)) {
  // would delete the trash itself: refuse or enumerate children instead
  throw new IOException("Refusing to delete ancestor of trash: " + path);
}
fs.delete(path, true);

Try / catch

try {
  fs.delete(path, true);
} catch (IOException e) {
  if (e.getMessage() != null && e.getMessage().contains("contains the trash")) {
    // delete children individually, or use -skipTrash deliberately
  }
}

Prevention

When it happens

Trigger: '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.

Common situations: 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.

Related errors


AI-assisted analysis of apache/hadoop@2add963021 (2026-08-22). Data as JSON: /api/errors/d5db0c665d3a44e3. Report an issue: GitHub.