apache/hadoop · error · IOException

It is not allowed to rename a parent directory: %s to its su

Error message

It is not allowed to rename a parent directory: %s to its subdirectory: %s

What it means

rename walks dst's parent chain; if src itself appears among dst's ancestors (dst lies inside src, e.g. rename('/a','/a/b/c')) it throws IOException. Moving a directory into its own subtree would create a cycle, so CosN rejects it before any destination-existence checks run. The loop terminates with dstParentPath non-null exactly when src is an ancestor of dst.

Source

Thrown at hadoop-cloud-storage-project/hadoop-cos/src/main/java/org/apache/hadoop/fs/cosn/CosNFileSystem.java:639

          + "the same file or directory");
    }

    // It is not allowed to rename a parent directory to its subdirectory
    Path dstParentPath;
    for (dstParentPath = dst.getParent();
         null != dstParentPath && !src.equals(dstParentPath);
         dstParentPath = dstParentPath.getParent()) {
      // Recursively find the common parent path of the source and
      // destination paths.
      LOG.debug("Recursively find the common parent directory of the source "
              + "and destination paths. The currently found parent path: {}",
          dstParentPath);
    }

    if (null != dstParentPath) {
      LOG.debug("It is not allowed to rename a parent directory:[{}] "
          + "to its subdirectory:[{}].", src, dst);
      throw new IOException(String.format(
          "It is not allowed to rename a parent directory: %s "
              + "to its subdirectory: %s", src, dst));
    }

    FileStatus dstFileStatus;
    try {
      dstFileStatus = this.getFileStatus(dst);

      // The destination path exists and is a file,
      // and the rename operation is not allowed.
      if (dstFileStatus.isFile()) {
        throw new FileAlreadyExistsException(String.format(
            "File: %s already exists", dstFileStatus.getPath()));
      } else {
        // The destination path is an existing directory,
        // and it is checked whether there is a file or directory
        // with the same name as the source path under the destination path
        dst = new Path(dst, src.getName());

View on GitHub (pinned to 2add963021)

Solutions

  1. Choose a destination outside the source subtree (e.g. /archive/day instead of /data/day/archive when renaming /data/day).
  2. If nesting is genuinely required, copy with FileUtil.copy(fs, src, fs, dst, true, conf) and then delete the source.
  3. Validate before calling: walk dst's parents and fail fast if any equals src.

Example fix

// before
fs.rename(new Path('/data/day=22'), new Path('/data/day=22/archive')); // IOException

// after
fs.rename(new Path('/data/day=22'), new Path('/archive/day=22'));
Defensive patterns

Strategy: validation

Validate before calling

for (Path anc = dst.getParent(); anc != null; anc = anc.getParent()) {
  if (anc.equals(src)) {
    throw new IllegalArgumentException('Destination lies inside source: ' + dst);
  }
}

Try / catch

try {
  fs.rename(src, dst);
} catch (IOException e) {
  if (e.getMessage() != null && e.getMessage().contains('subdirectory')) {
    // pick a destination outside the subtree, or copy+delete
    FileUtil.copy(fs, src, fs, outsideDst, true, conf);
    fs.delete(src, true);
  } else { throw e; }
}

Prevention

When it happens

Trigger: fs.rename(new Path('/a'), new Path('/a/b/c')); archive/rollup jobs moving a date directory into a subdirectory of itself; destination built by appending a suffix under the source path (e.g. /logs into /logs/archive).

Common situations: Archive scripts computing destination as source path + suffix; misordered arguments in a move utility; log rotation moving a directory into its own archive subfolder.

Related errors


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