alibaba/canal · error · SocketException
Socket already closed.
Error message
Socket already closed.
What it means
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.
Source
Thrown at driver/src/main/java/com/alibaba/otter/canal/parse/driver/mysql/socket/BioSocketChannel.java:43
private InputStream input;
private OutputStream output;
private final boolean ssl;
BioSocketChannel(Socket socket) throws IOException{
this.socket = socket;
this.input = new BufferedInputStream(socket.getInputStream(), 16384);
this.output = socket.getOutputStream();
this.ssl = (socket instanceof SSLSocket);
}
public void write(byte[]... buf) throws IOException {
OutputStream output = this.output;
if (output != null) {
for (byte[] bs : buf) {
output.write(bs);
}
} else {
throw new SocketException("Socket already closed.");
}
}
public byte[] read(int readSize) throws IOException {
InputStream input = this.input;
byte[] data = new byte[readSize];
int remain = readSize;
if (input == null) {
throw new SocketException("Socket already closed.");
}
while (remain > 0) {
try {
int read = input.read(data, readSize - remain, remain);
if (read > -1) {
remain -= read;
} else {
throw new IOException("EOF encountered.");
}View on GitHub (pinned to 87be50e876)
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.
Example fix
// before
channel.write(payload); // throws if channel was closed
// after
if (!channel.isConnected()) {
channel = reopen();
}
channel.write(payload); Defensive patterns
Strategy: validation
Validate before calling
if (channel == null || !channel.isConnected()) { /* reopen or fail */ } Type guard
public static boolean isWritableChannel(BioSocketChannel ch) {
return ch != null && ch.isConnected();
} Try / catch
try {
channel.write(payload);
} catch (java.net.SocketException e) {
if ("Socket already closed.".equals(e.getMessage())) { /* reopen + retry once */ }
throw e;
} Prevention
- Check isConnected() before every write.
- Never share a channel across reconnect cycles; create a fresh one.
- Stop writer threads before closing the socket.
When it happens
Trigger: 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.
Common situations: 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.
Related errors
- socket is closed !
- EOF encountered.
- Timeout occurred, failed to read total {} bytes in {} millis
- write failed ! please checking !
- socket has Interrupted !
AI-assisted analysis of alibaba/canal@87be50e876 (2026-08-14).
Data as JSON: /api/errors/7ae4df4bced1a951.
Report an issue: GitHub.