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
- 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
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
- 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
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
- Rename destination {dst} already exists.
- Source {src} and destination {dst} must both be directories
- Rename cannot overwrite non empty destination directory {dst
- Rename destination parent {parent} is a file.
- File system does not support symlinks
AI-assisted analysis of apache/hadoop@2add963021 (2026-08-22).
Data as JSON: /api/errors/17bebc2afbb521a0.
Report an issue: GitHub.