alibaba/canal · error · IOException
EOF encountered.
Error message
EOF encountered.
What it means
Thrown by BioSocketChannel.read(int) when the underlying InputStream.read returns -1, meaning the peer (MySQL server) closed its side of the TCP connection before the requested bytes arrived. This is a genuine end-of-stream on the socket, not a timeout. The read loop treats read == -1 as a hard EOF.
Source
Thrown at driver/src/main/java/com/alibaba/otter/canal/parse/driver/mysql/socket/BioSocketChannel.java:60
} 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.");
}
} catch (SocketTimeoutException te) {
if (Thread.interrupted()) {
throw new ClosedByInterruptException();
}
}
}
return data;
}
public byte[] read(int readSize, int timeout) throws IOException {
InputStream input = this.input;
byte[] data = new byte[readSize];
int remain = readSize;
int accTimeout = 0;
if (input == null) {
throw new SocketException("Socket already closed.");
}View on GitHub (pinned to 87be50e876)
Solutions
- Treat EOF as a trigger to reconnect from the last recorded binlog position (GTID/file+pos).
- Increase server-side wait_timeout / interactive_timeout and keep the connection active with periodic pings.
- Enable TCP keepalive (setKeepAlive is already true) and tune keepalive intervals for NAT environments.
- Inspect the server error log and any error packet received just before EOF to find the root cause.
- Wrap the read in retry logic bounded by a backoff policy.
Example fix
// before
byte[] body = channel.read(bodyLen); // EOF -> IOException
// after
try {
byte[] body = channel.read(bodyLen);
} catch (IOException eof) {
if (eof.getMessage().contains("EOF")) {
reconnectFromLastPosition();
return;
}
throw eof;
} Defensive patterns
Strategy: retry
Validate before calling
// EOF cannot be pre-validated without reading; pre-check only open state
if (channel == null || !channel.isConnected()) { reconnectFromLastPosition(); } Try / catch
try {
byte[] body = channel.read(bodyLen);
} catch (java.io.IOException e) {
if (e.getMessage() != null && e.getMessage().contains("EOF")) {
reconnectFromLastPosition();
} else throw e;
} Prevention
- Persist the last binlog position so EOF triggers a clean reconnect-and-resume.
- Send periodic heartbeats/PINGs to keep the connection alive.
- Tune wait_timeout and TCP keepalive for the network environment.
When it happens
Trigger: MySQL server closed the connection (normal quit, server shutdown, KILL of the binlog dump thread), a network device/intermediary severed the link, or the server sent an error packet and dropped the connection mid-read.
Common situations: Long-lived binlog dump connection dropped by a server idle timeout, wait_timeout, or a load balancer; MySQL restart; the binlog client requested an invalid position and the server closed after sending an error; firewall/NAT reaping idle connections.
Related errors
- Socket already closed.
- Timeout occurred, failed to read total {} bytes in {} millis
- end of stream when reading header
- socket is closed !
- write failed ! please checking !
AI-assisted analysis of alibaba/canal@87be50e876 (2026-08-14).
Data as JSON: /api/errors/77c2e7f5d9697c91.
Report an issue: GitHub.