apache/seatunnel · error · FileNotFoundException

Path ${file} is a directory.

Error message

Path ${file} is a directory.

What it means

SeaTunnelFTPFileSystem.open() first resolves the file status; if the target path is a directory it throws java.io.FileNotFoundException with 'Path <file> is a directory.' This guards the read path because directories cannot be opened as input streams.

Source

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

            }
            if (filePath.startsWith(workDir.toUri().getPath())) {
                return path;
            }
            // delete '/'
            return new Path(workDir, filePath.substring(1));
        }
        return new Path(workDir, path);
    }

    @Override
    public FSDataInputStream open(Path file, int bufferSize) throws IOException {
        FTPClient client = connect();
        Path workDir = new Path(client.printWorkingDirectory());
        Path absolute = makeAbsolute(workDir, file);
        FileStatus fileStat = getFileStatus(client, absolute);
        if (fileStat.isDirectory()) {
            disconnect(client);
            throw new FileNotFoundException("Path " + file + " is a directory.");
        }
        client.allocate(bufferSize);
        Path parent = absolute.getParent();
        // Change to parent directory on the
        // server. Only then can we read the
        // file
        // on the server by opening up an InputStream. As a side effect the working
        // directory on the server is changed to the parent directory of the file.
        // The FTP client connection is closed when close() is called on the
        // FSDataInputStream.
        client.changeWorkingDirectory(parent.toUri().getPath());
        InputStream is = client.retrieveFileStream(file.getName());
        FSDataInputStream fis = new FSDataInputStream(new FTPInputStream(is, client, statistics));
        if (!FTPReply.isPositivePreliminary(client.getReplyCode())) {
            // The ftpClient is an inconsistent state. Must close the stream
            // which in turn will logout and disconnect from FTP server
            fis.close();
            throw new IOException("Unable to open file: " + file + ", Aborting");

View on GitHub (pinned to cf67b549a7)

Solutions

  1. Point open()/the config at an actual file path, not a directory
  2. List the parent directory to confirm the intended file exists
  3. Remove the trailing slash or directory component from the path

Example fix

// before
fs.open(new Path("ftp://host/data/reports")) // a directory
// after
fs.open(new Path("ftp://host/data/reports/2024.csv"))
Defensive patterns

Strategy: validation

Validate before calling

// Java
FileSystem fs = path.getFileSystem(conf);
FileStatus st = fs.getFileStatus(path);
if (st.isDirectory()) throw new IllegalArgumentException("Expected a file, got directory: " + path);

Try / catch

try {
    return fs.open(path);
} catch (FileNotFoundException e) {
    if (e.getMessage() != null && e.getMessage().endsWith("is a directory.")) {
        throw new IllegalArgumentException("Point the source at a file, not a directory: " + path, e);
    }
    throw e;
}

Prevention

When it happens

Trigger: Calling open(file) where getFileStatus() reports the path as a directory — e.g. the configured path in the source points at a directory rather than a file, or a filename match pattern resolved to a directory.

Common situations: SeaTunnel file source configured with a directory path where a concrete file was expected; path constructed with a trailing slash; a rename/move left the expected file missing and a directory of the same name present.

Related errors


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