{"record":{"id":"88cc6807057919bd","repo":"apache/seatunnel","slug":"client-not-connected","errorCode":null,"errorMessage":"Client not connected","messagePattern":"Client not connected","errorType":"exception","errorClass":"FTPException","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":384,"sourceCode":"        if (parent == null || !mkdirs(client, parent, FsPermission.getDirDefault())) {\n            parent = (parent == null) ? new Path(\"/\") : parent;\n            disconnect(client);\n            throw new IOException(\"create(): Mkdirs failed to create: \" + parent);\n        }\n        client.allocate(bufferSize);\n        // Change to parent directory on the server. Only then can we write to the\n        // file on the server by opening up an OutputStream. As a side effect the\n        // working directory on the server is changed to the parent directory of the\n        // file. The FTP client connection is closed when close() is called on the\n        // FSDataOutputStream.\n        client.changeWorkingDirectory(parent.toUri().getPath());\n        FSDataOutputStream fos =\n                new FSDataOutputStream(client.storeFileStream(file.getName()), statistics) {\n                    @Override\n                    public void close() throws IOException {\n                        super.close();\n                        if (!client.isConnected()) {\n                            throw new FTPException(\"Client not connected\");\n                        }\n                        boolean cmdCompleted = client.completePendingCommand();\n                        disconnect(client);\n                        if (!cmdCompleted) {\n                            throw new FTPException(\n                                    \"Could not complete transfer, Reply Code - \"\n                                            + client.getReplyCode());\n                        }\n                    }\n                };\n        if (!FTPReply.isPositivePreliminary(client.getReplyCode())) {\n            // The ftpClient is an inconsistent state. Must close the stream\n            // which in turn will logout and disconnect from FTP server\n            fos.close();\n            throw new IOException(\"Unable to create file: \" + file + \", Aborting\");\n        }\n        return fos;\n    }","sourceCodeStart":366,"sourceCodeEnd":402,"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#L366-L402","documentation":"The FSDataOutputStream returned by create() wraps the FTP storeFileStream; on close(), if the underlying FTPClient is no longer connected it throws FTPException('Client not connected') instead of completing the upload, because the transfer's final status cannot be confirmed.","triggerScenarios":"Closing the output stream after the FTP control connection dropped — server-side idle timeout, network interruption, server restarting, or the connection killed by a firewall during the write.","commonSituations":"Long pauses while writing a large file causing the FTP server to time out the idle control connection; unstable network between client and server; server-enforced connection limits dropping the session.","solutions":["Investigate why the control connection dropped (server idle timeout, firewall, server restart) and adjust server-side timeouts","Write data continuously or use smaller files so the connection does not idle out mid-transfer","Enable FTP keep-alive if available and confirm the client host is not NAT-idled; retry the whole upload on a fresh connection","Check FTP server logs around the failure time for the disconnect cause"],"exampleFix":"// before\n// long client-side pause between writes -> server times out control connection\n// after\n// stream data promptly; wrap upload in retry with fresh create() on IOException/FTPException","handlingStrategy":"try-catch","validationCode":"// Monitor control-connection health before/during long uploads\nFTPClient c = connect();\nif (!c.isConnected() || c.getReplyCode() == 421) {\n    throw new IllegalStateException(\"FTP control connection unhealthy before upload\");\n}","typeGuard":null,"tryCatchPattern":"try (FSDataOutputStream out = fs.create(path, true)) {\n    out.write(data); // write promptly, avoid long idle gaps\n} catch (FTPException | IOException e) {\n    if (e.getMessage() != null && e.getMessage().contains(\"Client not connected\")) {\n        // retry whole upload on a fresh connection\n        retryUpload(path, data);\n    } else { throw e; }\n}","preventionTips":["Raise the server's idle control-connection timeout for large uploads","Avoid long client-side pauses between writes; buffer locally and stream continuously","Retry uploads from scratch on a fresh connection after any drop","Check for firewalls/NAT that kill idle TCP sessions mid-transfer"],"tags":["ftp","network","connection","stream"],"backgroundTag":"ftp-connection-dropped","analyzedSha":"cf67b549a7a6c35fa0beb12d83c62892427ea919","analyzedAt":"2026-09-10T21:44:55.265Z","contentChangedAt":"2026-09-10T21:44:55.265Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}