{"record":{"id":"17bebc2afbb521a0","repo":"apache/hadoop","slug":"cannot-rename-symlink-src-to-its-target-dst","errorCode":null,"errorMessage":"Cannot rename symlink {src} to its target {dst}","messagePattern":"Cannot rename symlink (.+?) to its target (.+?)","errorType":"exception","errorClass":"FileAlreadyExistsException","httpStatus":null,"severity":"error","filePath":"hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/fs/AbstractFileSystem.java","lineNumber":867,"sourceCode":"      boolean overwrite) throws AccessControlException,\n      FileAlreadyExistsException, FileNotFoundException,\n      ParentNotDirectoryException, UnresolvedLinkException, IOException {\n    // Default implementation deals with overwrite in a non-atomic way\n    final FileStatus srcStatus = getFileLinkStatus(src);\n\n    FileStatus dstStatus;\n    try {\n      dstStatus = getFileLinkStatus(dst);\n    } catch (IOException e) {\n      dstStatus = null;\n    }\n    if (dstStatus != null) {\n      if (dst.equals(src)) {\n        throw new FileAlreadyExistsException(\n            \"The source \"+src+\" and destination \"+dst+\" are the same\");\n      }\n      if (srcStatus.isSymlink() && dst.equals(srcStatus.getSymlink())) {\n        throw new FileAlreadyExistsException(\n            \"Cannot rename symlink \"+src+\" to its target \"+dst);\n      }\n      // It's OK to rename a file to a symlink and vice versa\n      if (srcStatus.isDirectory() != dstStatus.isDirectory()) {\n        throw new IOException(\"Source \" + src + \" and destination \" + dst\n            + \" must both be directories\");\n      }\n      if (!overwrite) {\n        throw new FileAlreadyExistsException(\"Rename destination \" + dst\n            + \" already exists.\");\n      }\n      // Delete the destination that is a file or an empty directory\n      if (dstStatus.isDirectory()) {\n        RemoteIterator<FileStatus> list = listStatusIterator(dst);\n        if (list != null && list.hasNext()) {\n          throw new IOException(\n              \"Rename cannot overwrite non empty destination directory \" + dst);\n        }","sourceCodeStart":849,"sourceCodeEnd":885,"githubUrl":"https://github.com/apache/hadoop/blob/2add9630210752f88ceb1bb74eb65e37bf41da8e/hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/fs/AbstractFileSystem.java#L849-L885","documentation":"Thrown by the overwrite-aware rename validation in AbstractFileSystem.renameInternal when the source is a symlink and the destination both exists and is exactly that symlink's stored target (dst.equals(srcStatus.getSymlink())). Renaming a link onto its own target is rejected because it would redefine the very path the link resolves to, making the result ambiguous. It surfaces as FileAlreadyExistsException from FileContext.rename(src, dst, Options.Rename.OVERWRITE).","triggerScenarios":"FileContext.rename(link, target, Rename.OVERWRITE) where getFileLinkStatus(link).isSymlink() is true, target exists, and target.equals(getSymlink(link)); e.g. rename /d/cur onto /d/real when /d/cur -> /d/real. The same check fires for the no-option rename once dst exists.","commonSituations":"Deployment scripts that keep a 'current' symlink to a versioned directory and then try to rename the link onto the version it points at; pipelines that normalize symlinked paths before moving them; LocalFs unit tests that build symlink fixtures and move them around.","solutions":["Rename to a destination that is not the symlink's own target, or delete the link first (fc.delete(src, false)) and rename the underlying file explicitly","If the intent is to replace the target's content, resolve the link (fc.getLinkTarget(src) / fc.getFileStatus(src)) and rename the resolved real path instead of the link","In idempotent job reruns, clean up stale 'current' links before the rename step"],"exampleFix":"// before\nfc.rename(new Path(\"/d/cur\"), new Path(\"/d/real\"), Options.Rename.OVERWRITE);\n// cur is a symlink -> /d/real : FileAlreadyExistsException\n\n// after\nFileStatus st = fc.getFileLinkStatus(src);\nif (st.isSymlink() && dst.equals(st.getSymlink())) {\n  fc.delete(src, false); // drop the link, move the real file instead\n}\nfc.rename(realSrc, dst, Options.Rename.OVERWRITE);","handlingStrategy":"validation","validationCode":"FileStatus st = fc.getFileLinkStatus(src);\nif (st.isSymlink()\n    && fc.util().exists(dst)\n    && dst.equals(st.getSymlink())) {\n  // pick a different destination, or fc.delete(src, false) first\n}","typeGuard":null,"tryCatchPattern":"catch (FileAlreadyExistsException e) { /* choose a new dst or delete the symlink, then retry the rename once */ }","preventionTips":["Never rename a symlink onto its own stored target; compute dst from the resolved real path","Before overwrite renames, compare getFileLinkStatus(src).getSymlink() with dst and abort early","In rerunnable jobs, remove stale 'current'/'latest' pointer links before the move step"],"tags":["rename","symlink","filealreadyexists","filecontext","hadoop-fs"],"backgroundTag":"rename-destination-invalid","analyzedSha":"2add9630210752f88ceb1bb74eb65e37bf41da8e","analyzedAt":"2026-08-22T19:55:07.957Z","schemaVersion":2},"datasetVersion":"2026-08-22T20:17:22.307Z"}