apache/hadoop · error · FileAlreadyExistsException

Cannot rename symlink {src} to its target {dst}

Error message

Cannot rename symlink {src} to its target {dst}

What it means

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).

Source

Thrown at hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/fs/AbstractFileSystem.java:867

      boolean overwrite) throws AccessControlException,
      FileAlreadyExistsException, FileNotFoundException,
      ParentNotDirectoryException, UnresolvedLinkException, IOException {
    // Default implementation deals with overwrite in a non-atomic way
    final FileStatus srcStatus = getFileLinkStatus(src);

    FileStatus dstStatus;
    try {
      dstStatus = getFileLinkStatus(dst);
    } catch (IOException e) {
      dstStatus = null;
    }
    if (dstStatus != null) {
      if (dst.equals(src)) {
        throw new FileAlreadyExistsException(
            "The source "+src+" and destination "+dst+" are the same");
      }
      if (srcStatus.isSymlink() && dst.equals(srcStatus.getSymlink())) {
        throw new FileAlreadyExistsException(
            "Cannot rename symlink "+src+" to its target "+dst);
      }
      // It's OK to rename a file to a symlink and vice versa
      if (srcStatus.isDirectory() != dstStatus.isDirectory()) {
        throw new IOException("Source " + src + " and destination " + dst
            + " must both be directories");
      }
      if (!overwrite) {
        throw new FileAlreadyExistsException("Rename destination " + dst
            + " already exists.");
      }
      // Delete the destination that is a file or an empty directory
      if (dstStatus.isDirectory()) {
        RemoteIterator<FileStatus> list = listStatusIterator(dst);
        if (list != null && list.hasNext()) {
          throw new IOException(
              "Rename cannot overwrite non empty destination directory " + dst);
        }

View on GitHub (pinned to 2add963021)

Solutions

  1. 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
  2. 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
  3. In idempotent job reruns, clean up stale 'current' links before the rename step

Example fix

// before
fc.rename(new Path("/d/cur"), new Path("/d/real"), Options.Rename.OVERWRITE);
// cur is a symlink -> /d/real : FileAlreadyExistsException

// after
FileStatus st = fc.getFileLinkStatus(src);
if (st.isSymlink() && dst.equals(st.getSymlink())) {
  fc.delete(src, false); // drop the link, move the real file instead
}
fc.rename(realSrc, dst, Options.Rename.OVERWRITE);
Defensive patterns

Strategy: validation

Validate before calling

FileStatus st = fc.getFileLinkStatus(src);
if (st.isSymlink()
    && fc.util().exists(dst)
    && dst.equals(st.getSymlink())) {
  // pick a different destination, or fc.delete(src, false) first
}

Try / catch

catch (FileAlreadyExistsException e) { /* choose a new dst or delete the symlink, then retry the rename once */ }

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Related errors


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