apache/seatunnel · error · FileNotFoundException
FTP directory does not exist
Error message
FTP directory does not exist: ${mask(absolute)} What it means
Thrown by SeaTunnelFTPFileSystem.list() as a FileNotFoundException when client.changeWorkingDirectory() fails for the requested directory, meaning the FTP server reports the directory does not exist (or is not accessible) at the given absolute path.
Solutions
- Verify the exact directory path exists with an FTP client (ls/cwd) using the same credentials
- Check path case sensitivity and leading/trailing slashes in the connector config
- Ensure the FTP user's home/permission allows entering the directory
- Pre-check with fs.exists(dir) before listing
- If the path can legitimately disappear, catch FileNotFoundException and treat as empty
Example fix
// before
FileStatus[] files = fs.listStatus(new Path("/remote/Data"));
// after
Path dir = new Path("/remote/data"); // match server case exactly
if (fs.exists(dir)) {
FileStatus[] files = fs.listStatus(dir);
} Defensive patterns
Strategy: try-catch
Validate before calling
Path dir = new Path("/remote/data");
if (!fs.exists(dir)) {
throw new IllegalArgumentException("FTP directory missing: " + dir);
} Try / catch
try {
FileStatus[] files = fs.listStatus(dir);
} catch (FileNotFoundException e) {
if (e.getMessage().contains("FTP directory does not exist")) {
// handle missing dir as empty input or fail with a clear config error
} else { throw e; }
} Prevention
- Verify directory paths (exact case, slashes) with an FTP client using the same credentials
- Pre-check fs.exists() before listing
- Remember FTP servers are case-sensitive and user home dirs change path resolution
- Catch FileNotFoundException where an absent directory is an acceptable input state
When it happens
Trigger: Listing a directory whose path does not exist on the FTP server; path typo or wrong relative path resolved against the FTP working directory; the directory was deleted between the existence check and the list; user lacks permission to enter the directory (CWD rejected).
Common situations: SeaTunnel FTP source configured with a remote path that does not exist; case-sensitivity mismatch (Linux FTP servers are case-sensitive); path containing a symlink or mount not present on the server; credentials scoped to a different home directory than expected.
Understand the failure class
Background: "File not found" and ENOENT errors: why libraries can't find a file that should exist — this error's family across 50 libraries.
Related errors
- File does not exist.
- FTP LIST failed for path=
- Path is a directory.
- Agent config is not a readable file
- Can't make directory for path
AI-assisted analysis of apache/seatunnel@cf67b549a7 (2026-09-10).
Data as JSON: /api/errors/7747d8cb0972c335.
Report an issue: GitHub.
Appendix: source
Thrown at seatunnel-connectors-v2/connector-file/connector-file-ftp/src/main/java/org/apache/seatunnel/connectors/seatunnel/file/ftp/system/SeaTunnelFTPFileSystem.java:532
private static final int PAGE_SIZE = 1_000;
private final FTPClient client;
private FtpListingSession(FTPClient client) {
this.client = client;
}
@Override
public FileStatus getFileStatus(Path path) throws IOException {
return SeaTunnelFTPFileSystem.this.getFileStatus(client, path);
}
@Override
public void list(Path directory, FileStatusConsumer consumer) throws IOException {
Path workDir = new Path(client.printWorkingDirectory());
Path absolute = makeAbsolute(workDir, directory);
String previousDirectory = client.printWorkingDirectory();
if (!client.changeWorkingDirectory(absolute.toUri().getPath())) {
throw new FileNotFoundException("FTP directory does not exist: " + mask(absolute));
}
Throwable failure = null;
try {
FTPListParseEngine engine = client.initiateListParsing();
if (!FTPReply.isPositiveCompletion(client.getReplyCode())) {
throw new IOException(
"FTP LIST failed for path="
+ mask(absolute)
+ ", replyCode="
+ client.getReplyCode());
}
while (engine.hasNext()) {
FTPFile[] files = engine.getNext(PAGE_SIZE);
int skipped = emitFileStatuses(files, absolute, consumer);
if (skipped > 0) {
LOG.warn(
"Skipped "
+ skippedView on GitHub (pinned to cf67b549a7)