{"record":{"id":"0b64369f2276bad4","repo":"apache/hadoop","slug":"can-not-copy-a-directory-to-a-subdirectory-of-self","errorCode":null,"errorMessage":"can not copy a directory to a subdirectory of self","messagePattern":"can not copy a directory to a subdirectory of self","errorType":"exception","errorClass":"IOException","httpStatus":null,"severity":"error","filePath":"hadoop-cloud-storage-project/hadoop-cos/src/main/java/org/apache/hadoop/fs/cosn/CosNFileSystem.java","lineNumber":720,"sourceCode":"  private boolean copyFile(Path srcPath, Path dstPath) throws IOException {\n    String srcKey = pathToKey(srcPath);\n    String dstKey = pathToKey(dstPath);\n    this.store.copy(srcKey, dstKey);\n    return true;\n  }\n\n  private boolean copyDirectory(Path srcPath, Path dstPath) throws IOException {\n    String srcKey = pathToKey(srcPath);\n    if (!srcKey.endsWith(PATH_DELIMITER)) {\n      srcKey += PATH_DELIMITER;\n    }\n    String dstKey = pathToKey(dstPath);\n    if (!dstKey.endsWith(PATH_DELIMITER)) {\n      dstKey += PATH_DELIMITER;\n    }\n\n    if (dstKey.startsWith(srcKey)) {\n      throw new IOException(\n          \"can not copy a directory to a subdirectory of self\");\n    }\n\n    this.store.storeEmptyFile(dstKey);\n    CosNCopyFileContext copyFileContext = new CosNCopyFileContext();\n\n    int copiesToFinishes = 0;\n    String priorLastKey = null;\n    do {\n      PartialListing objectList = this.store.list(\n          srcKey, Constants.COS_MAX_LISTING_LENGTH, priorLastKey, true);\n      for (FileMetadata file : objectList.getFiles()) {\n        this.boundedCopyThreadPool.execute(new CosNCopyFileTask(\n            this.store,\n            file.getKey(),\n            dstKey.concat(file.getKey().substring(srcKey.length())),\n            copyFileContext));\n        copiesToFinishes++;","sourceCodeStart":702,"sourceCodeEnd":738,"githubUrl":"https://github.com/apache/hadoop/blob/2add9630210752f88ceb1bb74eb65e37bf41da8e/hadoop-cloud-storage-project/hadoop-cos/src/main/java/org/apache/hadoop/fs/cosn/CosNFileSystem.java#L702-L738","documentation":"A last-line defense inside copyDirectory: before copying, if the destination key starts with the source key (the destination directory lies inside the source directory), it throws IOException. The public rename() normally rejects this case earlier via its parent-into-subdirectory check, so seeing this message usually means an internal path or a key-layout edge (directory marker keys with trailing '/') slipped past the earlier checks.","triggerScenarios":"Renaming a directory into its own subtree when earlier equality/ancestor checks were bypassed; key normalization differences where dstKey only differs from srcKey by a trailing-slash marker, making startsWith true.","commonSituations":"Edge-case renames near the bucket root; nested destinations computed directly from the source path string.","solutions":["Move the destination outside the source subtree.","If nesting is required, copy then delete the source instead of rename.","If src and dst are clearly not nested, report it as a CosN key-normalization bug with both keys."],"exampleFix":"// before\nfs.rename(new Path('/proj'), new Path('/proj/backup')); // reaches copyDirectory, throws\n\n// after\nfs.rename(new Path('/proj'), new Path('/backup/proj'));","handlingStrategy":"validation","validationCode":"// key-level check mirroring the guard\nString srcKey = src.toUri().getPath().startsWith('/') ? src.toUri().getPath().substring(1) : src.toUri().getPath();\nString dstKey = dst.toUri().getPath().startsWith('/') ? dst.toUri().getPath().substring(1) : dst.toUri().getPath();\nif ((dstKey + '/').startsWith(srcKey.endsWith('/') ? srcKey : srcKey + '/')) {\n  throw new IllegalArgumentException('Destination inside source subtree');\n}","typeGuard":null,"tryCatchPattern":"try {\n  fs.rename(src, dst);\n} catch (IOException e) {\n  if (e.getMessage() != null && e.getMessage().contains('subdirectory of self')) {\n    // move outside the subtree, or copy+delete\n    FileUtil.copy(fs, src, fs, outsideDst, true, conf);\n    fs.delete(src, true);\n  } else { throw e; }\n}","preventionTips":["Keep destination trees disjoint from source subtrees","Prefer copy+delete when nesting is unavoidable","If keys are plainly not nested, capture both keys and report a CosN bug"],"tags":["cosn","hadoop","rename","copy-directory","subdirectory"],"backgroundTag":"rename-into-own-subdirectory","analyzedSha":"2add9630210752f88ceb1bb74eb65e37bf41da8e","analyzedAt":"2026-08-22T19:55:07.957Z","schemaVersion":2},"datasetVersion":"2026-08-22T20:17:22.307Z"}