{"record":{"id":"b5acc52f478f839c","repo":"apache/hadoop","slug":"source-is-root-directory","errorCode":null,"errorMessage":"source is root directory","messagePattern":"source is root directory","errorType":"exception","errorClass":"RenameFailedException","httpStatus":null,"severity":"warning","filePath":"hadoop-tools/hadoop-aws/src/main/java/org/apache/hadoop/fs/s3a/S3AFileSystem.java","lineNumber":2435,"sourceCode":"   * @param src qualified path to be renamed\n   * @param dst qualified path after rename\n   * @return the source and (possibly null) destination status entries.\n   * @throws RenameFailedException if some criteria for a state changing\n   * rename was not met. This means work didn't happen; it's not something\n   * which is reported upstream to the FileSystem APIs, for which the semantics\n   * of \"false\" are pretty vague.\n   * @throws FileNotFoundException there's no source file.\n   * @throws IOException on IO failure.\n   */\n  @Retries.RetryTranslated\n  private Pair<S3AFileStatus, S3AFileStatus> initiateRename(\n      final Path src,\n      final Path dst) throws IOException {\n    String srcKey = pathToKey(src);\n    String dstKey = pathToKey(dst);\n\n    if (srcKey.isEmpty()) {\n      throw new RenameFailedException(src, dst, \"source is root directory\");\n    }\n    if (dstKey.isEmpty()) {\n      throw new RenameFailedException(src, dst, \"dest is root directory\");\n    }\n\n    // get the source file status; this raises a FNFE if there is no source\n    // file.\n    S3AFileStatus srcStatus = innerGetFileStatus(src, true,\n        StatusProbeEnum.ALL);\n\n    if (srcKey.equals(dstKey)) {\n      LOG.debug(\"rename: src and dest refer to the same file or directory: {}\",\n          dst);\n      throw new RenameFailedException(src, dst,\n          \"source and dest refer to the same file or directory\")\n          .withExitCode(srcStatus.isFile());\n    }\n","sourceCodeStart":2417,"sourceCodeEnd":2453,"githubUrl":"https://github.com/apache/hadoop/blob/2add9630210752f88ceb1bb74eb65e37bf41da8e/hadoop-tools/hadoop-aws/src/main/java/org/apache/hadoop/fs/s3a/S3AFileSystem.java#L2417-L2453","documentation":"initiateRename() maps src to an S3 key; an empty srcKey means the source is the bucket root (s3a://bucket/), which cannot be renamed. RenameFailedException 'source is root directory' is thrown; rename() catches it, logs at INFO and returns the exception's exit code (false here), so callers observe rename(...) returning false rather than an IOException.","triggerScenarios":"rename(new Path(\"/\"), dst) or any src that qualifies to the bucket root; src computed via getParent() on a top-level file.","commonSituations":"Directory-walking code that ends up renaming a parent; attempts to 'move the whole bucket' by renaming its root; off-by-one path arithmetic landing on root.","solutions":["Fix the caller to rename a non-root source path","To relocate everything under root, list the root and rename each child individually","Guard with src.isRoot() before calling rename and fail with a clear message"],"exampleFix":"// before\nboolean ok = fs.rename(new Path(\"/\"), dst);\n\n// after: refuse root sources up front\nif (src.isRoot()) {\n  throw new IllegalArgumentException(\"cannot rename the bucket root: \" + src);\n}\nboolean ok = fs.rename(src, dst);","handlingStrategy":"validation","validationCode":"if (src.isRoot()) {\n  throw new IllegalArgumentException(\"cannot rename bucket root as source\");\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Validate !src.isRoot() in rename wrappers","Compute sources from listings of children rather than parents of top-level entries","Check rename's boolean return - root-source failures surface as false with an INFO log, not as exceptions"],"tags":["s3a","rename","root","invalid-argument"],"backgroundTag":"rename-invalid-path","analyzedSha":"2add9630210752f88ceb1bb74eb65e37bf41da8e","analyzedAt":"2026-08-22T19:55:07.957Z","schemaVersion":2},"datasetVersion":"2026-08-22T20:17:22.307Z"}