apache/hadoop · error · IOException
Rename dir to self is forbidden
Error message
Rename dir to self is forbidden
What it means
In getDstUri, when the source is a directory the destination is normalized to directory form (UriPaths.toDirectory, adding the trailing '/') and then compared to src; if they are equal the rename is rejected because renaming a directory to itself is a forbidden no-op rather than a silent success.
Source
Thrown at hadoop-cloud-storage-project/hadoop-gcp/src/main/java/org/apache/hadoop/fs/gs/GoogleCloudStorageFileSystem.java:591
// After applying the rules, we will be left with 2 paths such that:
// -- either both are files or both are directories
// -- src exists and dst leaf does not exist
if (srcInfo.isDirectory()) {
// -- if src is a directory
// -- dst is an existing file => disallowed
// -- dst is a directory => rename the directory.
// The first case (dst is an existing file) is already checked earlier.
// If the destination path looks like a file, make it look like a
// directory path. This is because users often type 'mv foo bar'
// rather than 'mv foo bar/'.
if (!dstInfo.isDirectory()) {
dst = UriPaths.toDirectory(dst);
}
// Throw if renaming directory to self - this is forbidden
if (src.equals(dst)) {
throw new IOException("Rename dir to self is forbidden");
}
URI dstRelativeToSrc = src.relativize(dst);
// Throw if dst URI relative to src is not equal to dst,
// because this means that src is a parent directory of dst
// and src cannot be "renamed" to its subdirectory
if (!dstRelativeToSrc.equals(dst)) {
throw new IOException("Rename to subdir is forbidden");
}
if (dstInfo.exists()) {
dst =
dst.equals(GCSROOT)
? UriPaths.fromStringPathComponents(
srcItemName, /* objectName= */ null, /* allowEmptyObjectName= */ true)
: UriPaths.toDirectory(dst.resolve(srcItemName));
}
} else {View on GitHub (pinned to 2add963021)
Solutions
- Skip the rename when the normalized source and destination are equal (compare UriPaths.toDirectory of both).
- Choose a genuinely different destination directory name.
- When constructing destinations, never derive dst purely from src plus formatting.
Example fix
// before
Path src = new Path("gs://bucket/foo");
Path dst = new Path("gs://bucket/foo/"); // normalized to src -> forbidden
fs.rename(src, dst);
// after
URI s = UriPaths.toDirectory(src.toUri());
URI d = UriPaths.toDirectory(dst.toUri());
if (!s.equals(d)) {
fs.rename(src, dst);
} Defensive patterns
Strategy: validation
Validate before calling
URI srcDir = UriPaths.toDirectory(src.toUri());
URI dstDir = UriPaths.toDirectory(dst.toUri());
if (!srcDir.equals(dstDir)) {
fs.rename(src, dst);
} Try / catch
catch IOException with message equals("Rename dir to self is forbidden") - treat as a no-op success or fix destination construction; do not retry. Prevention
- Normalize both sides (trailing '/') before comparing src and dst in rename wrappers.
- Avoid building destinations from the source path plus optional formatting characters.
When it happens
Trigger: rename(gs://b/foo, gs://b/foo) or rename(gs://b/foo, gs://b/foo/) - the trailing slash is normalized away, making dst equal src. Any dst differing from src only by a trailing '/'.
Common situations: Shell-style 'mv dir dir/' habits encoded into scripts; destination built programmatically from the source path plus an optional separator; copy-pasted paths that accidentally reuse the source as destination.
Understand the failure class
- Authentication and authorization failures — expired tokens, bad credentials, and missing scopes.
Related errors
- Cannot rename because path does not exist: %s
- Rename to subdir is forbidden
- Cannot overwrite an existing file: %s
- Cannot create directories because of existing file: %s
- Wrong scheme: %s, in path: %s, expected scheme: %s
AI-assisted analysis of apache/hadoop@2add963021 (2026-08-22).
Data as JSON: /api/errors/b09a9a64f117e989.
Report an issue: GitHub.