apache/hadoop · error · IOException

Rename destination {} is a directory or file under source {}

Error message

Rename destination {} is a directory or file under source {}

What it means

IOException when the destination lies strictly inside the source subtree: dst.startsWith(src) and the character right after src is '/'. Moving a directory into itself is structurally impossible, so validateDestination rejects it. The separator check matters: src=/a does not block dst=/ab, only dst=/a/... - replicate exactly this check when pre-validating.

Source

Thrown at hadoop-hdfs-project/hadoop-hdfs/src/main/java/org/apache/hadoop/hdfs/server/namenode/FSDirRenameOp.java:558

  }

  private static void validateDestination(
      String src, String dst, INode srcInode)
      throws IOException {
    String error;
    if (srcInode.isSymlink() &&
        dst.equals(srcInode.asSymlink().getSymlinkString())) {
      throw new FileAlreadyExistsException("Cannot rename symlink " + src
          + " to its target " + dst);
    }
    // dst cannot be a directory or a file under src
    if (dst.startsWith(src)
        && dst.charAt(src.length()) == Path.SEPARATOR_CHAR) {
      error = "Rename destination " + dst
          + " is a directory or file under source " + src;
      NameNode.stateChangeLog.warn("DIR* FSDirectory.unprotectedRenameTo: "
          + error);
      throw new IOException(error);
    }

    if (FSDirectory.isExactReservedName(src)
        || FSDirectory.isExactReservedName(dst)) {
      error = "Cannot rename to or from /.reserved";
      throw new InvalidPathException(error);
    }
  }

  private static void validateOverwrite(
      String src, String dst, boolean overwrite, INode srcInode, INode dstInode)
      throws IOException {
    String error;// It's OK to rename a file to a symlink and vice versa
    if (dstInode.isDirectory() != srcInode.isDirectory()) {
      error = "Source " + src + " and destination " + dst
          + " must both be directories";
      NameNode.stateChangeLog.warn("DIR* FSDirectory.unprotectedRenameTo: "
          + error);

View on GitHub (pinned to 2add963021)

Solutions

  1. Move to a sibling or outside the subtree: `hdfs dfs -mv /data /data_archive` (or /archive/data).
  2. If nesting is the end state, rename the parent elsewhere first, then restructure.
  3. Pre-validate: reject dst equal to src or starting with src + '/'.

Example fix

# before
hdfs dfs -mv /data /data/archive   # IOException: destination under source

# after
hdfs dfs -mv /data /archive/data
Defensive patterns

Strategy: validation

Validate before calling

static boolean isStrictlyUnder(String ancestor, String path) {
  return path.length() > ancestor.length()
      && path.startsWith(ancestor)
      && path.charAt(ancestor.length()) == '/';
}
String s = src.toUri().getPath();
String d = dst.toUri().getPath();
if (s.equals(d) || isStrictlyUnder(s, d)) {
  throw new IOException("Destination is inside the source subtree: " + d);
}
fs.rename(src, dst);

Try / catch

try {
  fs.rename(src, dst);
} catch (IOException e) {
  if (e.getMessage() != null && e.getMessage().contains("under source")) {
    // move to a sibling path instead, e.g. dst = new Path(src.getParent(), archiveName)
  }
  throw e;
}

Prevention

When it happens

Trigger: `hdfs dfs -mv /data /data/archive`; destination built as src + '/' + suffix; archiving a directory into its own child; configs where the archive root defaults to inside the source tree.

Common situations: Archive-in-place patterns (the archive must be a sibling or outside path); templated destinations that accidentally prefix the source; rotation scripts computing dst from src components.

Related errors


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