apache/seatunnel · error · IOException

Cannot rename %s under itself : %s

Error message

Cannot rename %s under itself : %s

What it means

rename() guards against a degenerate move: if the destination path lies underneath the source path (isParentOf(absoluteSrc, absoluteDst)), moving a directory into itself is impossible, so it throws a plain IOException 'Cannot rename %s under itself : %s'.

Source

Thrown at seatunnel-connectors-v2/connector-file/connector-file-ftp/src/main/java/org/apache/seatunnel/connectors/seatunnel/file/ftp/system/SeaTunnelFTPFileSystem.java:845

     * @throws IOException IOException
     */
    private boolean rename(FTPClient client, Path src, Path dst) throws IOException {
        Path workDir = new Path(client.printWorkingDirectory());
        Path absoluteSrc = makeAbsolute(workDir, src);
        Path absoluteDst = makeAbsolute(workDir, dst);
        if (!exists(client, absoluteSrc)) {
            throw new FileNotFoundException("Source path " + src + " does not exist");
        }
        if (isDirectory(absoluteDst)) {
            // destination is a directory: rename goes underneath it with the
            // source name
            absoluteDst = new Path(absoluteDst, absoluteSrc.getName());
        }
        if (exists(client, absoluteDst)) {
            throw new FileAlreadyExistsException("Destination path " + dst + " already exists");
        }
        if (isParentOf(absoluteSrc, absoluteDst)) {
            throw new IOException(
                    "Cannot rename " + absoluteSrc + " under itself" + " : " + absoluteDst);
        }
        String from = absoluteSrc.toUri().getPath();
        String to = absoluteDst.toUri().getPath();
        return client.rename(from, to);
    }

    @Override
    public Path getWorkingDirectory() {
        // Return home directory always since we do not maintain state.
        return getHomeDirectory();
    }

    @Override
    public Path getHomeDirectory() {
        FTPClient client = null;
        try {
            client = connect();

View on GitHub (pinned to cf67b549a7)

Solutions

  1. Fix the destination path computation so dst is not inside src
  2. Move the source to a sibling/temp path first, then relocate it to the final nested location in two steps
  3. Review config that derives the output path from the input path; add an assert src is not an ancestor of dst before calling rename

Example fix

// before
fs.rename(new Path("/data"), new Path("/data/archive/data")); // invalid
// after
Path src = new Path("/data");
Path dst = new Path("/data-new"); // sibling, not a descendant
fs.rename(src, dst);
Defensive patterns

Strategy: validation

Validate before calling

// Java: reject nested rename before calling the API
Path src = new Path("/data");
Path dst = new Path("/data/archive");
if (dst.toString().startsWith(src.toString() + "/")) {
    throw new IllegalArgumentException("Cannot rename "+src+" under itself");
}

Try / catch

try {
    fs.rename(src, dst);
} catch (IOException e) {
    if (e.getMessage().contains("under itself")) {
        logger.error("Path construction bug: dst is inside src");
    }
}

Prevention

When it happens

Trigger: rename(client, src, dst) where absoluteDst is a subpath of absoluteSrc — e.g. renaming '/data' to '/data/archive/data' or renaming a dir to a path inside itself.

Common situations: Programmatic path construction bug where the output directory is computed from the input directory; misconfigured staging dir located inside the directory being renamed; recursive backup logic that nests the source under its own child.

Understand the failure class

Background: "Must be a positive integer", "Invalid value", "Unsupported": the invalid-argument-value error family, when a library rejects the value you pass — this error's family across 35 libraries.

Related errors


AI-assisted analysis of apache/seatunnel@cf67b549a7 (2026-09-10). Data as JSON: /api/errors/e8e5d0aa89512712. Report an issue: GitHub.