apache/seatunnel · error · IOException

Failed to create directory %s in %s, FTP reply code: %d, rep

Error message

Failed to create directory %s in %s, FTP reply code: %d, reply string: %s

What it means

SeaTunnelFTPFileSystem wraps Apache Commons Net FTPClient to provide HDFS-like filesystem semantics over FTP. When creating a directory via makeDirectory, an FTP server may return false even though the code intends success; the code double-checks existence and throws this IOException with the raw FTP reply code and string so the operator can diagnose the server-side rejection.

Source

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

        }

        // Create the directory
        String pathName = absolute.getName();
        String parentDir = parent != null ? parent.toUri().getPath() : "/";

        // Change to parent directory
        if (!client.changeWorkingDirectory(parentDir)) {
            throw new IOException(
                    String.format(
                            "Failed to change working directory to %s, FTP reply code: %d, reply string: %s",
                            parentDir, client.getReplyCode(), client.getReplyString()));
        }
        // Create directory
        boolean created = client.makeDirectory(pathName);
        if (!created) {
            // Double check if directory was actually created (some FTP servers don't return true)
            if (!exists(client, absolute)) {
                throw new IOException(
                        String.format(
                                "Failed to create directory %s in %s, FTP reply code: %d, reply string: %s",
                                pathName,
                                parentDir,
                                client.getReplyCode(),
                                client.getReplyString()));
            }
        }
        return true;
    }

    /**
     * Convenience method, so that we don't open a new connection when using this method from within
     * another method. Otherwise every API invocation incurs the overhead of opening/closing a TCP
     * connection.
     */
    private boolean isFile(FTPClient client, Path file) {
        try {

View on GitHub (pinned to cf67b549a7)

Solutions

  1. Check the FTP reply code in the message: 550 usually means permission denied or file exists — verify the FTP user has write permission on the parent directory
  2. Ensure the parent directory exists before writing; create parent directories first
  3. Verify the path is absolute and correct relative to the FTP user's home directory (FTP servers resolve relative paths against the login dir)
  4. Connect to the FTP server manually (ftp/lftp) and run MKD on the same path to see the raw server reply
  5. Check server disk quota / filesystem full on the FTP host

Example fix

// before
boolean created = client.makeDirectory("data/output"); // relative path, may resolve unexpectedly
// after
String absolutePath = "/home/ftpuser/data/output";
client.changeWorkingDirectory("/");
for (String part : absolutePath.split("/")) {
    if (!part.isEmpty() && !client.makeDirectory(part) && !client.changeWorkingDirectory(part)) {
        throw new IOException("Cannot create or enter " + part + ": " + client.getReplyString());
    }
}
Defensive patterns

Strategy: validation

Validate before calling

// Java: pre-check FTP write access before job run
FTPClient c = new FTPClient();
c.connect(host, port);
c.login(user, pass);
String dir = path.substring(0, path.lastIndexOf('/'));
if (!c.changeWorkingDirectory(dir)) {
    throw new IllegalStateException("Cannot access FTP dir " + dir + ": " + c.getReplyString());
}
c.disconnect();

Type guard

// Java
static boolean canWriteToFtpDir(FTPClient client, String dir) throws IOException {
    return client.changeWorkingDirectory(dir) && FTPReply.isPositiveCompletion(client.getReplyCode());
}

Try / catch

try {
    ftpFileSystem.mkdir(path);
} catch (IOException e) {
    logger.error("FTP mkdir failed (check reply code in message, likely permission/missing parent): {}", path, e);
    throw new RuntimeException("FTP directory creation failed for " + path, e);
}

Prevention

When it happens

Trigger: client.makeDirectory(pathName) returns false AND a subsequent exists(client, absolute) check confirms the directory was not created; the IOException is thrown at SeaTunnelFTPFileSystem.java:758 with client.getReplyCode()/getReplyString() embedded.

Common situations: FTP user lacks write permission on the parent directory; parent directory itself does not exist (some servers refuse nested creation); server in a read-only or quota-exceeded state; path violates server-side naming rules; relative path resolved against an unexpected working directory.

Understand the failure class

Background: "failed to write file", "Could not save figure", "Error saving remote file" — file write failed: causes and fixes across languages and libraries — this error's family across 38 libraries.

Related errors


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