{"record":{"id":"761d5cffc8dfd903","repo":"apache/seatunnel","slug":"failed-to-create-directory-s-in-s-ftp-reply-cod","errorCode":null,"errorMessage":"Failed to create directory %s in %s, FTP reply code: %d, reply string: %s","messagePattern":"Failed to create directory (.+?) in (.+?), FTP reply code: (.+?), reply string: (.+?)","errorType":"exception","errorClass":"IOException","httpStatus":null,"severity":"error","filePath":"seatunnel-connectors-v2/connector-file/connector-file-ftp/src/main/java/org/apache/seatunnel/connectors/seatunnel/file/ftp/system/SeaTunnelFTPFileSystem.java","lineNumber":758,"sourceCode":"        }\n\n        // Create the directory\n        String pathName = absolute.getName();\n        String parentDir = parent != null ? parent.toUri().getPath() : \"/\";\n\n        // Change to parent directory\n        if (!client.changeWorkingDirectory(parentDir)) {\n            throw new IOException(\n                    String.format(\n                            \"Failed to change working directory to %s, FTP reply code: %d, reply string: %s\",\n                            parentDir, client.getReplyCode(), client.getReplyString()));\n        }\n        // Create directory\n        boolean created = client.makeDirectory(pathName);\n        if (!created) {\n            // Double check if directory was actually created (some FTP servers don't return true)\n            if (!exists(client, absolute)) {\n                throw new IOException(\n                        String.format(\n                                \"Failed to create directory %s in %s, FTP reply code: %d, reply string: %s\",\n                                pathName,\n                                parentDir,\n                                client.getReplyCode(),\n                                client.getReplyString()));\n            }\n        }\n        return true;\n    }\n\n    /**\n     * Convenience method, so that we don't open a new connection when using this method from within\n     * another method. Otherwise every API invocation incurs the overhead of opening/closing a TCP\n     * connection.\n     */\n    private boolean isFile(FTPClient client, Path file) {\n        try {","sourceCodeStart":740,"sourceCodeEnd":776,"githubUrl":"https://github.com/apache/seatunnel/blob/cf67b549a7a6c35fa0beb12d83c62892427ea919/seatunnel-connectors-v2/connector-file/connector-file-ftp/src/main/java/org/apache/seatunnel/connectors/seatunnel/file/ftp/system/SeaTunnelFTPFileSystem.java#L740-L776","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["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","Ensure the parent directory exists before writing; create parent directories first","Verify the path is absolute and correct relative to the FTP user's home directory (FTP servers resolve relative paths against the login dir)","Connect to the FTP server manually (ftp/lftp) and run MKD on the same path to see the raw server reply","Check server disk quota / filesystem full on the FTP host"],"exampleFix":"// before\nboolean created = client.makeDirectory(\"data/output\"); // relative path, may resolve unexpectedly\n// after\nString absolutePath = \"/home/ftpuser/data/output\";\nclient.changeWorkingDirectory(\"/\");\nfor (String part : absolutePath.split(\"/\")) {\n    if (!part.isEmpty() && !client.makeDirectory(part) && !client.changeWorkingDirectory(part)) {\n        throw new IOException(\"Cannot create or enter \" + part + \": \" + client.getReplyString());\n    }\n}","handlingStrategy":"validation","validationCode":"// Java: pre-check FTP write access before job run\nFTPClient c = new FTPClient();\nc.connect(host, port);\nc.login(user, pass);\nString dir = path.substring(0, path.lastIndexOf('/'));\nif (!c.changeWorkingDirectory(dir)) {\n    throw new IllegalStateException(\"Cannot access FTP dir \" + dir + \": \" + c.getReplyString());\n}\nc.disconnect();","typeGuard":"// Java\nstatic boolean canWriteToFtpDir(FTPClient client, String dir) throws IOException {\n    return client.changeWorkingDirectory(dir) && FTPReply.isPositiveCompletion(client.getReplyCode());\n}","tryCatchPattern":"try {\n    ftpFileSystem.mkdir(path);\n} catch (IOException e) {\n    logger.error(\"FTP mkdir failed (check reply code in message, likely permission/missing parent): {}\", path, e);\n    throw new RuntimeException(\"FTP directory creation failed for \" + path, e);\n}","preventionTips":["Verify FTP user write permissions on the target directory with a manual MKD test before configuring jobs","Use absolute paths starting with '/' to avoid working-directory resolution surprises","Create parent directories explicitly rather than relying on recursive creation","Monitor FTP server quota and disk space"],"tags":["ftp","file-write-failed","io"],"backgroundTag":"file-write-failed","analyzedSha":"cf67b549a7a6c35fa0beb12d83c62892427ea919","analyzedAt":"2026-09-10T21:44:55.265Z","contentChangedAt":"2026-09-10T21:44:55.265Z","schemaVersion":2},"datasetVersion":"2026-09-14T11:17:12.474Z"}