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

  1. Treat EOF as a trigger to reconnect from the last recorded binlog position (GTID/file+pos).
  2. Increase server-side wait_timeout / interactive_timeout and keep the connection active with periodic pings.
  3. Enable TCP keepalive (setKeepAlive is already true) and tune keepalive intervals for NAT environments.
  4. Inspect the server error log and any error packet received just before EOF to find the root cause.
  5. 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

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


AI-assisted analysis of alibaba/canal@87be50e876 (2026-08-14). Data as JSON: /api/errors/77c2e7f5d9697c91. Report an issue: GitHub.