{"record":{"id":"7ae4df4bced1a951","repo":"alibaba/canal","slug":"socket-already-closed","errorCode":null,"errorMessage":"Socket already closed.","messagePattern":"Socket already closed\\.","errorType":"exception","errorClass":"SocketException","httpStatus":null,"severity":"error","filePath":"driver/src/main/java/com/alibaba/otter/canal/parse/driver/mysql/socket/BioSocketChannel.java","lineNumber":43,"sourceCode":"    private InputStream   input;\n    private OutputStream  output;\n    private final boolean ssl;\n\n    BioSocketChannel(Socket socket) throws IOException{\n        this.socket = socket;\n        this.input = new BufferedInputStream(socket.getInputStream(), 16384);\n        this.output = socket.getOutputStream();\n        this.ssl = (socket instanceof SSLSocket);\n    }\n\n    public void write(byte[]... buf) throws IOException {\n        OutputStream output = this.output;\n        if (output != null) {\n            for (byte[] bs : buf) {\n                output.write(bs);\n            }\n        } else {\n            throw new SocketException(\"Socket already closed.\");\n        }\n    }\n\n    public byte[] read(int readSize) throws IOException {\n        InputStream input = this.input;\n        byte[] data = new byte[readSize];\n        int remain = readSize;\n        if (input == null) {\n            throw new SocketException(\"Socket already closed.\");\n        }\n        while (remain > 0) {\n            try {\n                int read = input.read(data, readSize - remain, remain);\n                if (read > -1) {\n                    remain -= read;\n                } else {\n                    throw new IOException(\"EOF encountered.\");\n                }","sourceCodeStart":25,"sourceCodeEnd":61,"githubUrl":"https://github.com/alibaba/canal/blob/87be50e87686a3e8af08c368d0e1ffd1f59eb04a/driver/src/main/java/com/alibaba/otter/canal/parse/driver/mysql/socket/BioSocketChannel.java#L25-L61","documentation":"Thrown by BioSocketChannel.write when the output stream field is null, i.e. the channel has already been closed or never had its OutputStream initialized. write() guards on output != null and throws a SocketException otherwise. It indicates a use-after-close bug or writing on a channel whose construction failed.","triggerScenarios":"Calling write(...) on a BioSocketChannel after close() has run, or after the underlying Socket threw during getOutputStream in the constructor. Concurrent threads where one closes the channel while another writes.","commonSituations":"Connection-pool reuse of a channel that was evicted/closed; reconnect logic that forgets to null-check before retrying a write; a heartbeat thread writing after the parser thread closed the socket on error.","solutions":["Guard callers with isConnected()/an open flag before writing and reopen the channel if closed.","Make close() and write() mutually exclusive, or set output=null only under a lock and check under the same lock.","Discard pooled channels after an error rather than reusing them.","Ensure the constructor fully completes (socket connected) before exposing the channel to writer threads."],"exampleFix":"// before\nchannel.write(payload); // throws if channel was closed\n\n// after\nif (!channel.isConnected()) {\n    channel = reopen();\n}\nchannel.write(payload);","handlingStrategy":"validation","validationCode":"if (channel == null || !channel.isConnected()) { /* reopen or fail */ }","typeGuard":"public static boolean isWritableChannel(BioSocketChannel ch) {\n    return ch != null && ch.isConnected();\n}","tryCatchPattern":"try {\n    channel.write(payload);\n} catch (java.net.SocketException e) {\n    if (\"Socket already closed.\".equals(e.getMessage())) { /* reopen + retry once */ }\n    throw e;\n}","preventionTips":["Check isConnected() before every write.","Never share a channel across reconnect cycles; create a fresh one.","Stop writer threads before closing the socket."],"tags":["network","socket","io","lifecycle","use-after-close"],"backgroundTag":null,"analyzedSha":"87be50e87686a3e8af08c368d0e1ffd1f59eb04a","analyzedAt":"2026-08-14T04:30:11.918Z","schemaVersion":2},"datasetVersion":"2026-08-14T05:17:29.042Z"}