apache/hadoop · error · IOException
A file cannot be created in root.
Error message
A file cannot be created in root.
What it means
getDstUri throws IOException("A file cannot be created in root.") during rename when the source is a FILE and the destination equals GCSROOT (gs:// with no bucket). Root has no bucket to contain an object, so the move is impossible; directory sources proceed to other checks, and a missing destination parent is reported with a different message.
Source
Thrown at hadoop-cloud-storage-project/hadoop-gcp/src/main/java/org/apache/hadoop/fs/gs/GoogleCloudStorageFileSystem.java:555
private List<FileInfo> getFileInfos(List<URI> paths) throws IOException {
List<FileInfo> result = new ArrayList<>(paths.size());
for (URI path : paths) {
// TODO: Do this concurrently
result.add(getFileInfo(path));
}
return result;
}
private URI getDstUri(FileInfo srcInfo, FileInfo dstInfo, @Nullable FileInfo dstParentInfo)
throws IOException {
URI src = srcInfo.getPath();
URI dst = dstInfo.getPath();
// Throw if src is a file and dst == GCS_ROOT
if (!srcInfo.isDirectory() && dst.equals(GCSROOT)) {
throw new IOException("A file cannot be created in root.");
}
// Throw if the destination is a file that already exists, and it's not a source file.
if (dstInfo.exists() && !dstInfo.isDirectory() && (srcInfo.isDirectory() || !dst.equals(src))) {
throw new IOException("Cannot overwrite an existing file: " + dst);
}
// Rename operation cannot be completed if parent of destination does not exist.
if (dstParentInfo != null && !dstParentInfo.exists()) {
throw new IOException(
"Cannot rename because path does not exist: " + dstParentInfo.getPath());
}
// Leaf item of the source path.
String srcItemName = getItemName(src);
// Having taken care of the initial checks, apply the regular rules.
// After applying the rules, we will be left with 2 paths such that:View on GitHub (pinned to 2add963021)
Solutions
- Fix destination computation so dst always includes a bucket and a non-empty object name.
- Validate dst against the root URI (and for emptiness) before calling rename.
- For 'move to default bucket' semantics, resolve an explicit bucket path instead of gs://.
Example fix
// before
URI dst = UriPaths.getParentPath(srcPath); // walked above bucket -> gs://
fs.rename(srcPath, dst); // 'A file cannot be created in root.'
// after
checkArgument(!dst.equals(GCSROOT) && !dst.getPath().equals("/"), "dst must name a bucket + object");
fs.rename(srcPath, dst); Defensive patterns
Strategy: validation
Validate before calling
checkArgument(!dst.equals(GCSROOT) && dst.getBucket() != null
&& !dst.getObjectName().isEmpty(),
"rename destination must be bucket + object, got: %s", dst);
gcsFs.rename(src, dst); Prevention
- Validate that destination URIs always carry a bucket and non-empty object name.
- Never call getParentPath() past the bucket level — guard the walk with a bucket check.
- Reject empty destination strings at the configuration/API boundary.
When it happens
Trigger: rename(file, gs://) — the destination URI is exactly root, typically produced by parent-path arithmetic that walked above the bucket level (getParent on a bucket-root path) or an empty user-supplied destination.
Common situations: Path code stripping one segment too many; destination computed as getParent(src) of an already-bucket-level path; empty or misparsed destination URIs from configuration.
Related errors
- Rename to subdir is forbidden
- rename destination cannot be the root
- This operation is not supported across two different buckets
- Move destination must be different from source for %s.
- Cannot create a file whose name looks like a directory: '%s'
AI-assisted analysis of apache/hadoop@2add963021 (2026-08-22).
Data as JSON: /api/errors/e34bb748c557c91a.
Report an issue: GitHub.