NEW

Pulse 2025 Product Roundup: From Monitoring to AI-Native Control Plane

How to Fix MySQL Error 2006: MySQL Server Has Gone Away

ERROR 2006 (HY000): MySQL server has gone away (client error code CR_SERVER_GONE_ERROR) is a client-side error indicating that the TCP connection to the MySQL server was closed before the client expected it. The server did not refuse the connection — it already accepted it and then either timed it out, the server restarted, or the network dropped it.

Impact

The error surfaces as a database query failure from the application's perspective. Connection pools that do not validate connections before reuse return this error on the first query after the connection goes stale. This causes a class of "works fine, then fails after idle" bugs that are especially common in long-running processes (workers, cron jobs, scheduled tasks) that acquire a database connection once and reuse it across multiple cycles.

Common Causes

  1. wait_timeout or interactive_timeout expired: MySQL closed an idle connection after the configured idle time (default 8 hours, but many managed databases use 60–600 s)
  2. Query or transaction took longer than net_read_timeout or net_write_timeout (default 30 s each) — MySQL closes connections that stall mid-transfer
  3. A single packet exceeded max_allowed_packet — MySQL closes the connection after rejecting an oversized packet (see also: error 1153)
  4. MySQL server restarted, crashed, or ran out of memory and OOM-killed the mysqld process
  5. Network infrastructure (firewall, NAT gateway, load balancer) silently dropped the idle TCP connection
  6. Connection pool returned a connection to the pool that was already closed server-side, then handed it out again without validation
  7. Long-running mysqldump or import that the server killed due to timeout or resource limits
  8. Replication: replica applies a transaction that was larger than its max_allowed_packet
  9. Underlying OS-level keepalive not configured to detect dead TCP connections before MySQL's own timeout fires

Troubleshooting and Resolution Steps

  1. Check the timeout settings:

    SHOW VARIABLES LIKE 'wait_timeout';
    SHOW VARIABLES LIKE 'interactive_timeout';
    SHOW VARIABLES LIKE 'net_read_timeout';
    SHOW VARIABLES LIKE 'net_write_timeout';
    SHOW VARIABLES LIKE 'max_allowed_packet';
    

    wait_timeout governs non-interactive connections (typical application pools). interactive_timeout governs connections opened with the CLIENT_INTERACTIVE flag (the mysql CLI).

  2. Confirm whether the server restarted recently:

    SHOW GLOBAL STATUS LIKE 'Uptime';
    -- or
    SELECT * FROM performance_schema.global_status
    WHERE VARIABLE_NAME = 'Uptime';
    

    A low Uptime value relative to when the error started indicates a crash or restart.

  3. Check the MySQL error log for OOM kills, crashes, or assertion failures:

    SHOW VARIABLES LIKE 'log_error';
    

    Then inspect that file path on the server. Look for [ERROR], [Warning] Aborted connection, or Killed.

  4. Count aborted connections — a rising number indicates the problem is systematic:

    SHOW GLOBAL STATUS LIKE 'Aborted_connects';
    SHOW GLOBAL STATUS LIKE 'Aborted_clients';
    

    Aborted_clients increments when the client disconnects without a proper COM_QUIT; this is the typical signature of a pool returning a timed-out connection.

  5. Configure the connection pool to validate connections before use. For HikariCP (Java):

    connectionTestQuery=SELECT 1
    # or rely on JDBC4 isValid() — preferred
    keepaliveTime=30000      # ms — send a ping to keep the connection alive
    idleTimeout=60000        # ms — remove connections idle longer than this
    maxLifetime=1800000      # ms — recycle all connections every 30 min
    

    For SQLAlchemy (Python):

    engine = create_engine(
        DATABASE_URL,
        pool_pre_ping=True,        # validates connection before checkout
        pool_recycle=1800,         # recycle connections every 30 min
        pool_timeout=30,
    )
    

    pool_pre_ping=True issues a SELECT 1 before handing out each connection and discards it on failure, transparently reconnecting.

  6. Lower wait_timeout server-side so connections are reclaimed faster and pools are forced to reconnect on their schedule rather than accumulating stale handles:

    SET GLOBAL wait_timeout = 300;       -- 5 minutes
    SET PERSIST wait_timeout = 300;
    

    Match this to a value shorter than your pool's maxLifetime so the pool recycles before the server times out.

  7. Raise net_read_timeout for long-running imports or bulk operations:

    SET SESSION net_read_timeout = 300;
    SET SESSION net_write_timeout = 300;
    

    Use session-level changes, not global, so regular OLTP queries are not affected.

  8. Enable TCP keepalive at the OS level to detect dead connections across NAT gateways:

    # Linux — adjust in /etc/sysctl.conf for persistence
    sysctl -w net.ipv4.tcp_keepalive_time=60
    sysctl -w net.ipv4.tcp_keepalive_intvl=10
    sysctl -w net.ipv4.tcp_keepalive_probes=3
    

    This makes the OS send keepalive probes after 60 s of idle, so stateful firewalls with a 5-minute idle timeout will see traffic and not drop the connection. MySQL's own wait_timeout fires independently.

  9. Increase max_allowed_packet if the error occurs on large queries or imports:

    SET GLOBAL max_allowed_packet = 268435456;  -- 256 MB
    

    Check whether the error correlates with requests that send or receive large payloads.

  10. For worker processes and scheduled jobs, explicitly re-establish the connection at the start of each work unit rather than assuming the connection held between cycles is still alive:

    # Django example — close old connections before a long task
    from django.db import connection
    
    def run_batch():
        connection.close()  # force reconnect on next query
        # ... now run queries
    

    In Celery, use django.db.close_old_connections() in a task_prerun signal handler.

  11. Use --reconnect in the mysql CLI for interactive sessions that may be idle:

    mysql --reconnect -u root -p mydb
    

Additional Information

  • Error 2006 (CR_SERVER_GONE_ERROR) is a client-side code defined in errmsg.h; the server never sees it. Error 2013 (CR_SERVER_LOST) is similar — the server connection was lost mid-query rather than before the query started. Both have the same root causes and fixes.
  • Amazon RDS, Aurora, and Google Cloud SQL all default wait_timeout to much lower values than the MySQL default (often 60–600 s for serverless variants). Applications written against a local MySQL with the 8-hour default will break immediately when deployed to a managed database.
  • The pool_pre_ping / connectionTestQuery option adds one RTT to each connection checkout. For high-throughput OLTP, consider a longer keepaliveTime (to send periodic pings) combined with a shorter maxLifetime (to recycle) instead of a pre-ping on every checkout.
  • Galera Cluster nodes that fail a quorum check close all client connections — this looks identical to a timeout from the client's perspective.

Frequently Asked Questions

Q: The error only happens at night or on weekends. Why? A: Those are periods of low traffic when application connections are idle long enough to hit wait_timeout. The fix is either a shorter pool maxLifetime that recycles before the server times out, or pool_pre_ping=True.

Q: I set pool_pre_ping=True and still get the error occasionally. Why? A: pool_pre_ping checks the connection at checkout time. If the server closes the connection after the ping but before the actual query executes — possible under very short wait_timeout values or flapping networks — the error can still occur. Retry logic for 2006 is still necessary.

Q: The error occurs during a mysqldump. How do I fix it? A: Set --net-read-timeout and --net-write-timeout on the dump command, or use --single-transaction for InnoDB tables (which starts a transaction and uses snapshot reads, avoiding long per-table lock holds). Also check that max_allowed_packet is large enough for your biggest table row.

Q: Can the error happen if MySQL is still running? A: Yes. The most common non-restart cause is wait_timeout expiring on an idle connection held by the pool.

Q: How do I distinguish a timeout from a crash? A: Check SHOW GLOBAL STATUS LIKE 'Uptime'. If uptime reset recently, it was a restart. Also check Aborted_clients — if it's increasing steadily, that's idle timeouts. Check the error log for crash signatures (core dump path, assertion failure, signal 6/11).

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.