How to Fix MySQL Error 2013: Lost Connection to MySQL Server During Query

ERROR 2013 (HY000): Lost connection to MySQL server during query is raised when the server closes the connection while a query or statement is still in progress. The error symbol is CR_SERVER_LOST.

Impact

Unlike error 2006 (CR_SERVER_GONE_ERROR), which fires before a query is sent, error 2013 occurs mid-execution — the query was accepted by the server and then the connection was dropped before a response was returned. Any in-progress transaction is rolled back, and no result set or affected-row count is available to the client.

Developers most often encounter this on long-running queries (bulk imports, heavy aggregations, ALTER TABLE operations), large INSERT/LOAD DATA statements, or during application startup when connection pools are established against an overloaded or misconfigured server. In ORMs like SQLAlchemy, Django ORM, or ActiveRecord it typically surfaces as a OperationalError, DatabaseError, or Lost connection exception wrapping the 2013 code.

Common Causes

  1. net_read_timeout or net_write_timeout exceeded. The server waits only net_read_timeout seconds (default 30 s) for the client to finish sending a packet, and net_write_timeout seconds (default 60 s) to write a result packet. Long-running queries that stall mid-transfer can trigger either.

  2. wait_timeout / interactive_timeout expiry during a query. If the server's idle timeout fires while a slow query is executing (unusual but possible when the session sits between phases), the server forcibly closes the socket.

  3. max_allowed_packet exceeded. When a query or result set exceeds max_allowed_packet (default 64 MB in MySQL 8.0, 4 MB in older versions), the server drops the connection rather than sending a truncated response.

  4. Server OOM kill or crash. The MySQL process can be killed by the OS out-of-memory killer or crash mid-query due to an InnoDB assertion or storage-level error. The client sees 2013 because the socket closes unexpectedly.

  5. Network interruption or firewall timeout. Stateful firewalls and cloud security groups often drop TCP connections idle for more than 60–300 seconds. A query that takes longer than the firewall's idle threshold gets its socket silently dropped.

  6. MAX_EXECUTION_TIME optimizer hint or max_statement_time (MariaDB). In MySQL 5.7.8+ the MAX_EXECUTION_TIME hint kills a SELECT mid-flight; the client receives 2013.

  7. Replication or Group Replication failover. Automatic primary failover redirects the TCP connection; queries running at that moment are terminated.

Troubleshooting and Resolution Steps

  1. Check current timeout and packet-size settings:

    SHOW VARIABLES LIKE 'net_read_timeout';
    SHOW VARIABLES LIKE 'net_write_timeout';
    SHOW VARIABLES LIKE 'wait_timeout';
    SHOW VARIABLES LIKE 'max_allowed_packet';
    
  2. Increase timeouts for long-running workloads (set in my.cnf or my.ini for persistence):

    [mysqld]
    net_read_timeout  = 3600
    net_write_timeout = 3600
    wait_timeout      = 28800
    max_allowed_packet = 256M
    

    Or raise them for the current session only:

    SET SESSION net_read_timeout  = 3600;
    SET SESSION net_write_timeout = 3600;
    
  3. Identify slow or oversized queries using the slow query log:

    [mysqld]
    slow_query_log      = 1
    slow_query_log_file = /var/log/mysql/slow.log
    long_query_time     = 5
    

    Then inspect the log or query performance_schema:

    SELECT event_id, sql_text, timer_wait/1e12 AS seconds
    FROM performance_schema.events_statements_history_long
    ORDER BY timer_wait DESC
    LIMIT 10;
    
  4. Test for max_allowed_packet as the cause. If the error occurs only on large INSERT batches or LOAD DATA statements, check the size of the payload and raise max_allowed_packet. Also verify the client-side setting matches — many connectors have their own max_allowed_packet option.

  5. Check the MySQL error log for crashes or OOM events:

    sudo grep -E "(Aborted|killed|crash|OOM)" /var/log/mysql/error.log | tail -50
    

    On systemd systems: journalctl -u mysql --since "1 hour ago".

  6. Eliminate network/firewall timeouts. Enable TCP keepalives on the MySQL server:

    [mysqld]
    # Linux kernel will send keepalive probes after 300 s of idle
    # MySQL itself uses the OS setting; ensure it is lower than your firewall timeout
    

    In the MySQL client, use --connect-timeout and --net-read-timeout flags. For connection pools, set a keepalive_interval or issue a lightweight ping query (SELECT 1) before borrowing a connection.

  7. Verify MAX_EXECUTION_TIME is not set unexpectedly:

    SELECT @@SESSION.MAX_EXECUTION_TIME;  -- MySQL 5.7.8+
    -- Also check query hints in your ORM-generated SQL
    

Additional Information

  • Related error codes: 2006 (CR_SERVER_GONE_ERROR) fires before the query is sent (connection dropped between statements); 1317 (ER_QUERY_INTERRUPTED) is raised when a query is killed via KILL QUERY.
  • Client library behavior: The C client API returns CR_SERVER_LOST for both a lost mid-query connection and a failed reconnect. The MYSQL_OPT_RECONNECT option triggers one automatic reconnect attempt but cannot recover the interrupted transaction.
  • Connection pool implications: Many pools (HikariCP, c3p0, SQLAlchemy) catch 2013 and retire the connection. Configuring testOnBorrow / pool_pre_ping prevents stale connections from being handed to application code.
  • MySQL 8.0 vs 5.7: The default max_allowed_packet was raised from 4 MB to 64 MB in MySQL 8.0. If you migrated from 5.7 and started seeing 2013 errors with large payloads, confirm the server-side value was carried over.
  • MariaDB: Uses max_statement_time (in milliseconds) instead of MAX_EXECUTION_TIME and raises error 1969 (ER_STATEMENT_TIMEOUT) before closing the connection, which may or may not appear as 2013 depending on the client library version.

Frequently Asked Questions

What is the difference between MySQL error 2006 and error 2013? Error 2006 (CR_SERVER_GONE_ERROR) means the server was unreachable when the client tried to send a query — the connection was already dead. Error 2013 (CR_SERVER_LOST) means the server closed the connection while a query was actively running. 2013 is more disruptive because an in-flight transaction is lost.

Can I safely retry a query after a 2013 error? Only if the operation is idempotent or you are certain no partial write occurred. Because the server drops the connection, any uncommitted transaction is rolled back. Read-only SELECTs are always safe to retry. For writes, verify with a SELECT before re-issuing the INSERT/UPDATE to avoid duplicates.

Why does error 2013 happen only on large imports? Large LOAD DATA or multi-row INSERT statements produce big network packets. If the packet size exceeds max_allowed_packet, or if the transfer takes longer than net_read_timeout, the server aborts the connection. Increase both max_allowed_packet and net_read_timeout, and consider batching imports into smaller chunks.

Does enabling MYSQL_OPT_RECONNECT fix error 2013? It allows the client to silently reconnect after the error, which prevents the error from bubbling up to application code — but the interrupted query is not re-executed automatically. The application still needs to handle the lost transaction. In MySQL Connector/C 8.0.25+ MYSQL_OPT_RECONNECT is deprecated; connection pooling with health checks is the recommended alternative.

Subscribe to the Pulse Newsletter

Get early access to new Pulse features, insightful blogs & exclusive events , webinars, and workshops.

We use cookies to provide an optimized user experience and understand our traffic. To learn more, read our use of cookies; otherwise, please choose 'Accept Cookies' to continue using our website.