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

  1. Fix destination computation so dst always includes a bucket and a non-empty object name.
  2. Validate dst against the root URI (and for emptiness) before calling rename.
  3. 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

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


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