{"record":{"id":"b868cc3e8039b5cd","repo":"apache/hadoop","slug":"rename-destination-is-a-directory-or-file-under","errorCode":null,"errorMessage":"Rename destination {} is a directory or file under source {}","messagePattern":"Rename destination (.+?) is a directory or file under source (.+?)","errorType":"exception","errorClass":"IOException","httpStatus":null,"severity":"error","filePath":"hadoop-hdfs-project/hadoop-hdfs/src/main/java/org/apache/hadoop/hdfs/server/namenode/FSDirRenameOp.java","lineNumber":558,"sourceCode":"  }\n\n  private static void validateDestination(\n      String src, String dst, INode srcInode)\n      throws IOException {\n    String error;\n    if (srcInode.isSymlink() &&\n        dst.equals(srcInode.asSymlink().getSymlinkString())) {\n      throw new FileAlreadyExistsException(\"Cannot rename symlink \" + src\n          + \" to its target \" + dst);\n    }\n    // dst cannot be a directory or a file under src\n    if (dst.startsWith(src)\n        && dst.charAt(src.length()) == Path.SEPARATOR_CHAR) {\n      error = \"Rename destination \" + dst\n          + \" is a directory or file under source \" + src;\n      NameNode.stateChangeLog.warn(\"DIR* FSDirectory.unprotectedRenameTo: \"\n          + error);\n      throw new IOException(error);\n    }\n\n    if (FSDirectory.isExactReservedName(src)\n        || FSDirectory.isExactReservedName(dst)) {\n      error = \"Cannot rename to or from /.reserved\";\n      throw new InvalidPathException(error);\n    }\n  }\n\n  private static void validateOverwrite(\n      String src, String dst, boolean overwrite, INode srcInode, INode dstInode)\n      throws IOException {\n    String error;// It's OK to rename a file to a symlink and vice versa\n    if (dstInode.isDirectory() != srcInode.isDirectory()) {\n      error = \"Source \" + src + \" and destination \" + dst\n          + \" must both be directories\";\n      NameNode.stateChangeLog.warn(\"DIR* FSDirectory.unprotectedRenameTo: \"\n          + error);","sourceCodeStart":540,"sourceCodeEnd":576,"githubUrl":"https://github.com/apache/hadoop/blob/2add9630210752f88ceb1bb74eb65e37bf41da8e/hadoop-hdfs-project/hadoop-hdfs/src/main/java/org/apache/hadoop/hdfs/server/namenode/FSDirRenameOp.java#L540-L576","documentation":"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.","triggerScenarios":"`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.","commonSituations":"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.","solutions":["Move to a sibling or outside the subtree: `hdfs dfs -mv /data /data_archive` (or /archive/data).","If nesting is the end state, rename the parent elsewhere first, then restructure.","Pre-validate: reject dst equal to src or starting with src + '/'."],"exampleFix":"# before\nhdfs dfs -mv /data /data/archive   # IOException: destination under source\n\n# after\nhdfs dfs -mv /data /archive/data","handlingStrategy":"validation","validationCode":"static boolean isStrictlyUnder(String ancestor, String path) {\n  return path.length() > ancestor.length()\n      && path.startsWith(ancestor)\n      && path.charAt(ancestor.length()) == '/';\n}\nString s = src.toUri().getPath();\nString d = dst.toUri().getPath();\nif (s.equals(d) || isStrictlyUnder(s, d)) {\n  throw new IOException(\"Destination is inside the source subtree: \" + d);\n}\nfs.rename(src, dst);","typeGuard":null,"tryCatchPattern":"try {\n  fs.rename(src, dst);\n} catch (IOException e) {\n  if (e.getMessage() != null && e.getMessage().contains(\"under source\")) {\n    // move to a sibling path instead, e.g. dst = new Path(src.getParent(), archiveName)\n  }\n  throw e;\n}","preventionTips":["Compute archive destinations as siblings of the source, never as children.","Mirror the NameNode's exact check (startsWith + '/' separator) in path validators."],"tags":["hdfs","rename","subtree","path-validation"],"backgroundTag":"rename-into-own-subtree","analyzedSha":"2add9630210752f88ceb1bb74eb65e37bf41da8e","analyzedAt":"2026-08-22T19:55:07.957Z","schemaVersion":2},"datasetVersion":"2026-08-22T20:17:22.307Z"}