apache/hadoop · error · IOException

Cannot rename %s to %s, %s is a file

Error message

Cannot rename %s to %s, %s is a file

What it means

When the destination of a rename does not exist, CosN stats dst's parent; if that parent exists as a FILE, rename throws IOException('Cannot rename <src> to <dst>, <dstParent> is a file') — the destination cannot sit beneath a file (ENOTDIR). If the parent is missing entirely, getFileStatus(parent) instead propagates FileNotFoundException out of rename, which is a separate failure mode.

Source

Thrown at hadoop-cloud-storage-project/hadoop-cos/src/main/java/org/apache/hadoop/fs/cosn/CosNFileSystem.java:679

        } catch (FileNotFoundException e) {
          statuses = null;
        }
        if (null != statuses && statuses.length > 0) {
          LOG.debug("Cannot rename source file: [{}] to dest file: [{}], "
              + "because the file already exists.", src, dst);
          throw new FileAlreadyExistsException(
              String.format(
                  "File: %s already exists", dst
              )
          );
        }
      }
    } catch (FileNotFoundException e) {
      // destination path not exists
      Path tempDstParentPath = dst.getParent();
      FileStatus dstParentStatus = this.getFileStatus(tempDstParentPath);
      if (!dstParentStatus.isDirectory()) {
        throw new IOException(String.format(
            "Cannot rename %s to %s, %s is a file", src, dst, dst.getParent()
        ));
      }
      // The default root directory is definitely there.
    }

    boolean result;
    if (srcFileStatus.isDirectory()) {
      result = this.copyDirectory(src, dst);
    } else {
      result = this.copyFile(src, dst);
    }

    if (!result) {
      //Since rename is a non-atomic operation, after copy fails,
      // it is not allowed to delete the data of the original path.
      return false;
    } else {

View on GitHub (pinned to 2add963021)

Solutions

  1. Delete or relocate the file occupying the destination's parent path.
  2. After removing the conflict, fs.mkdirs(dst.getParent()) and retry the rename.
  3. Choose a destination whose full prefix chain contains only real directories.

Example fix

// before
fs.rename(new Path('/a/f'), new Path('/b/g')); // IOException: ... /b is a file

// after
Path parent = new Path('/b');
if (fs.exists(parent) && fs.getFileStatus(parent).isFile()) {
  fs.delete(parent, false);
}
fs.mkdirs(parent);
fs.rename(new Path('/a/f'), new Path('/b/g'));
Defensive patterns

Strategy: validation

Validate before calling

Path parent = dst.getParent();
if (fs.exists(parent) && !fs.getFileStatus(parent).isDirectory()) {
  throw new IllegalStateException('Destination parent is a file: ' + parent);
}
if (!fs.exists(parent)) { fs.mkdirs(parent); }

Try / catch

try {
  fs.rename(src, dst);
} catch (IOException e) {
  if (e.getMessage() != null && e.getMessage().contains('is a file')) {
    fs.delete(dst.getParent(), false);
    fs.mkdirs(dst.getParent());
    fs.rename(src, dst);
  } else { throw e; }
}

Prevention

When it happens

Trigger: fs.rename('/a/f', '/b/g') where '/b' is an existing file object; renaming into a 'directory' path that was previously created as a file.

Common situations: Destination tree half-created with files at directory levels; a partition path used both as file and directory across runs of a pipeline.

Related errors


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