MySQL Error 2013: Lost Connection During Query
Chat2DB TeamERROR 2013 (HY000): Lost connection to MySQL server during query is a client-side error. The client sent a statement (or was waiting for its result) and the network connection closed underneath it. The error number is in the 2000 range, which means it is generated by the client library, not by the server. The server never sent an error packet explaining why; it simply stopped talking.
That is what makes 2013 frustrating: the message is the same no matter what closed the connection. The server may have killed the thread, hit a timeout, rejected an oversized packet, crashed, or been restarted, or something in between (a proxy, load balancer, or firewall) may have dropped the TCP session. The fix depends entirely on which of these happened, so this guide focuses on finding the actual cause first.
Every error message and variable value below was reproduced on MySQL 8.4.11 in Docker, using the 8.4 mysql client and mysqldump.
Error 2013 vs Error 2006
The two errors are often confused because they have the same root: the connection is gone.
ERROR 2013 (HY000): Lost connection to MySQL server during queryis raised when the client was reading the reply and the connection broke.ERROR 2006 (HY000): MySQL server has gone awayis raised when the client finds the connection already unusable, typically while trying to send the next statement.
In practice the number you get depends on the client library and on exactly when the break is detected. In our tests with the 8.4 client, an idle connection that the server had killed, and an idle connection whose server had been restarted, both produced 2013 on the next statement, not 2006. Many drivers report the same situation as 2006. So do not rely on the number alone to decide what happened. Treat both as "the connection closed" and use the diagnostics below to find out why.
Since MySQL 8.0.24 there is also a third, much more specific error for one case. When the server closes a connection because it was idle longer than wait_timeout, clients that support it get:
ERROR 4031 (HY000): The client was disconnected by the server because of inactivity. See wait_timeout and interactive_timeout for configuring this behavior.If you see 4031, you already know the cause. If you see 2013 or 2006, keep reading.
Step 1: Check the Server Error Log
The fastest way to identify the cause is to look at the server side at the exact time of the error. First find where the log goes:
SHOW VARIABLES LIKE 'log_error%';On MySQL 8.4 the default log_error_verbosity is 2, which logs errors and warnings but not notes. The per-connection "Aborted connection" messages are notes, so they are hidden by default. Raise the verbosity while you investigate (this is dynamic and needs SYSTEM_VARIABLES_ADMIN):
SET GLOBAL log_error_verbosity = 3;Then reproduce the problem and look for lines like these, which we captured by triggering each cause on purpose:
[Note] [MY-010914] [Server] Aborted connection 19 to db: 'unconnected' user: 'root' host: '172.18.0.3' (Got timeout writing communication packets).
[Note] [MY-010914] [Server] Aborted connection 26 to db: 'demo' user: 'root' host: 'localhost' (Got a packet bigger than 'max_allowed_packet' bytes).
[Note] [MY-010914] [Server] Aborted connection 16 to db: 'unconnected' user: 'root' host: '172.18.0.3' (Got an error reading communication packets).
[Note] [MY-010914] [Server] Aborted connection 14 to db: 'unconnected' user: 'root' host: '172.18.0.3' (The client was disconnected by the server because of inactivity.).The text in parentheses maps directly to a cause:
| Reason in the error log | Likely cause |
|---|---|
Got timeout writing communication packets | Client stopped reading the result for longer than net_write_timeout |
Got timeout reading communication packets | Client stopped sending in the middle of a statement for longer than net_read_timeout |
Got a packet bigger than 'max_allowed_packet' bytes | A statement or row larger than max_allowed_packet |
Got an error reading communication packets | The client side closed the socket abruptly (client killed, proxy reset, network drop) |
The client was disconnected by the server because of inactivity | wait_timeout or interactive_timeout |
If there is no "Aborted connection" line at all, but you see a startup banner (ready for connections) shortly after the error time, the server restarted or crashed. That case is covered in Cause 5.
You can also watch the counter that increments whenever an established connection is closed improperly:
SHOW GLOBAL STATUS LIKE 'Aborted_clients';
SHOW GLOBAL STATUS LIKE 'Uptime';If Aborted_clients grows every time the application logs a 2013, the server saw the disconnect. A small Uptime means the server restarted recently.
Step 2: Check the Relevant Variables
These are the settings involved in most 2013 errors. Values shown are the MySQL 8.4 defaults we read from a fresh server:
SHOW VARIABLES WHERE Variable_name IN (
'net_read_timeout', 'net_write_timeout',
'wait_timeout', 'interactive_timeout',
'max_allowed_packet', 'connect_timeout', 'max_execution_time'
);| Variable | 8.4 default | What it controls |
|---|---|---|
net_read_timeout | 30 seconds | How long the server waits for more data from the client while reading a statement |
net_write_timeout | 60 seconds | How long the server waits for a blocked write to the client to proceed |
wait_timeout | 28800 seconds (8 hours) | Idle time before the server closes a non-interactive connection |
interactive_timeout | 28800 seconds | Same, for clients that connect with the interactive flag |
max_allowed_packet | 67108864 (64 MB) | Largest single packet, statement, or row the server accepts or sends |
connect_timeout | 10 seconds | Time allowed for the connection handshake |
max_execution_time | 0 (no limit) | Server-side limit for SELECT statements, in milliseconds |
Note that the 64 MB max_allowed_packet default applies to MySQL 8.0 and later. Older servers defaulted to much smaller values, which is why so many old answers about this error recommend raising it.
Cause 1: The Connection Was Killed
If someone (or something) runs KILL on the connection while a query is executing, the client gets 2013. We reproduced it by starting a long query and killing it from a second session:
-- Session A
SELECT SLEEP(30);
-- Session B
SELECT id, user, time, info
FROM information_schema.PROCESSLIST
WHERE info = 'SELECT SLEEP(30)';
KILL 42; -- the id from the previous querySession A gets:
ERROR 2013 (HY000): Lost connection to MySQL server during queryNote the difference with KILL QUERY 42, which stops only the statement and keeps the connection open; in that case the client does not get 2013.
Things that issue KILL automatically and are easy to forget:
- Query killers such as
pt-killor custom cron scripts that kill long-running statements. - Managed database services that terminate sessions during failover or maintenance.
- Replication or cluster tooling that kills connections on a demoted primary.
If a statement limit is what you actually want, prefer max_execution_time (or the MAX_EXECUTION_TIME optimizer hint), which gives a clear error and keeps the connection alive:
SELECT /*+ MAX_EXECUTION_TIME(1000) */ SLEEP(3) FROM orders;ERROR 3024 (HY000): Query execution was interrupted, maximum statement execution time exceededCause 2: The Client Read the Result Too Slowly (net_write_timeout)
When a query returns a large result set, the server pushes rows into the socket as fast as it can. If the client stops reading, the socket buffers fill, and the server's write blocks. After net_write_timeout seconds of blocking, the server gives up and closes the connection.
This happens with streaming or unbuffered result sets, where the application processes each row (calls an API, writes a file, does another query) before fetching the next. We reproduced it by lowering the timeout to 2 seconds and piping a 200,000-row result into a reader that paused for 8 seconds:
SET GLOBAL net_write_timeout = 2;mysql --quick -e "SELECT * FROM demo.big" | (sleep 8; cat)The client printed the rows it had already received, then:
ERROR 2013 (HY000) at line 1: Lost connection to MySQL server during queryAnd the server log said Got timeout writing communication packets.
Fix
- Keep the loop that consumes a streaming result fast. Write rows to a local buffer or file first, and do slow work afterwards.
- Page through large tables with keyset pagination (
WHERE id > ? ORDER BY id LIMIT 10000) instead of one huge streaming query. - If the slow consumer is intentional, raise the timeout for that session only:
SET SESSION net_write_timeout = 600;net_read_timeout is the mirror case: the server is reading a statement from the client and the client pauses mid-packet. It is rarer, but appears with slow LOAD DATA LOCAL INFILE sources or clients that stream large statements over a slow link.
mysqldump Already Raises These Timeouts
It is commonly suggested to raise net_write_timeout for dumps. We checked what the 8.4 mysqldump sends by enabling the general query log, and it already starts every dump with:
SET SESSION NET_READ_TIMEOUT= 86400, SESSION NET_WRITE_TIMEOUT= 86400In our test, a dump piped into a consumer that paused for 8 seconds completed fine even with the global net_write_timeout at 2 seconds. So if a long dump dies with 2013, server-side net timeouts are usually not the reason. Look at proxies and load balancers (Cause 4), at a server restart during the dump, or at the import side, where a large extended INSERT can exceed max_allowed_packet.
Cause 3: A Packet Bigger Than max_allowed_packet
Every statement is sent as one packet, and every result row is returned as one. If either exceeds max_allowed_packet, the connection is closed. We lowered the server limit to 1 MB and imported a file containing one 3 MB INSERT, followed by a simple SELECT 1:
SET GLOBAL max_allowed_packet = 1048576;mysql --force --max-allowed-packet=64M demo < big.sqlERROR 1153 (08S01) at line 1: Got a packet bigger than 'max_allowed_packet' bytes
ERROR 2013 (HY000) at line 2: Lost connection to MySQL server during queryThe first statement fails with 1153, and then the server closes the connection, so the next statement fails with 2013. In an application log you may only see the 2013 if the 1153 was swallowed or retried, which is why the server-side "Aborted connection ... Got a packet bigger than 'max_allowed_packet' bytes" note is so valuable. Older clients and drivers often report this case as 2006 instead.
Functions that build large values inside the server fail differently and do not drop the connection: REPEAT('a', 2000000) with a 1 MB limit returned ERROR 1301 (HY000): Result of repeat() was larger than max_allowed_packet (1048576) - truncated in our tests, an ordinary statement error rather than a disconnect.
Fix
Raise the limit on the server, and remember the client has its own limit too (the mysql client and mysqldump default to lower values, and accept --max-allowed-packet):
SET GLOBAL max_allowed_packet = 268435456; -- 256 MB, new connections only[mysqld]
max_allowed_packet = 256M
[mysqldump]
max_allowed_packet = 256MA new global value applies only to connections opened afterwards. Also consider whether large packets are necessary: storing multi-megabyte blobs in rows, or generating a single INSERT with hundreds of thousands of rows, can usually be split. mysqldump --net-buffer-length controls how large each extended INSERT becomes.
Cause 4: Proxies, Load Balancers, and NAT Idle Timeouts
When there is anything between the client and MySQL (an AWS NLB or ALB, HAProxy, ProxySQL, a Kubernetes service mesh, a corporate firewall, a NAT gateway), that device has its own idle timeout. When it expires, the device drops the session, often without telling either side. The next time the client uses the connection, it gets 2013 or 2006, while the MySQL server may still think the connection is open, or log Got an error reading communication packets.
Signs that a middlebox is the cause:
- Failures happen after a consistent idle period, such as a few minutes, much shorter than
wait_timeout. - The error only happens through the proxy or load balancer, never when connecting directly.
- Long-running queries that return nothing for a long time (a big
ALTER TABLE, an aggregate over a huge table) die at a consistent duration.
Fix
- Find the idle timeout of each hop and make sure the pool retires connections before the shortest one. In HikariCP that is
maxLifetimeandkeepaliveTime; in other pools, look for "max lifetime", "idle timeout", or "validation" settings. - Enable TCP keepalives on the client so idle connections send traffic periodically.
- For long silent queries through a load balancer, raise the load balancer idle timeout for the MySQL listener, or run the maintenance from a host that connects directly.
- Validate connections on borrow (most pools support a lightweight ping) so a dead connection is replaced instead of handed to the application.
Cause 5: The Server Crashed or Restarted
When the server process disappears, every active connection gets 2013. We simulated a crash by sending SIGKILL to the server while one client ran SELECT SLEEP(60) and another sat idle. Both clients reported:
ERROR 2013 (HY000): Lost connection to MySQL server during queryThe idle client got it on its next statement, not immediately.
Confirm a restart with:
SHOW GLOBAL STATUS LIKE 'Uptime';Then check why it restarted:
- The MySQL error log will show a new startup sequence ending in
ready for connections. After a clean shutdown there is aShutdown completeline just before it; after a crash, there is not, and InnoDB performs crash recovery during startup. - On Linux, an out-of-memory kill shows up in the kernel log, not in MySQL's log. Check
dmesg -T | grep -i -E "killed process|out of memory"orjournalctl -k. In Kubernetes, the pod status showsOOMKilled. - On managed services, check the provider's event log for failovers, maintenance, or storage events.
If memory is the problem, check the sum of innodb_buffer_pool_size and per-connection buffers against the memory limit of the host or container. A large max_connections combined with large sort_buffer_size or join_buffer_size values can push memory use well past the buffer pool.
Cause 6: The Connection Was Idle Too Long (wait_timeout)
wait_timeout does not interrupt queries; it closes connections that sit idle. The failure shows up on the first statement after the idle period. With an 8.0.24+ server and client you get the explicit 4031 error. We reproduced it by setting a 2 second timeout:
SET SESSION wait_timeout = 2;
SELECT 1;
-- wait more than 2 seconds
SELECT 2;ERROR 4031 (HY000): The client was disconnected by the server because of inactivity. See wait_timeout and interactive_timeout for configuring this behavior.Older servers or drivers report the same situation as 2006 or 2013. The fix is on the client side: configure the pool to retire or validate idle connections before wait_timeout expires, rather than raising wait_timeout to a very large value, which leaves abandoned connections occupying slots.
A Diagnostic Routine
When a 2013 appears in production, work through these steps:
- Note the exact time and the statement that failed.
- Check
Uptime. If it is smaller than the time since the error, investigate the restart first (Cause 5). - Set
log_error_verbosity = 3and find the matching "Aborted connection" line. The reason text tells you which cause applies. - If there is no server-side record, the connection was probably dropped between client and server (Cause 4).
- If the failure happens at a consistent duration, compare it with
net_write_timeout,net_read_timeout, the load balancer timeout, and any query killer thresholds. - If it only happens with large statements or imports, compare the size with
max_allowed_packeton both server and client. - Reset
log_error_verbosityto 2 when done if you do not want notes in the log permanently.
When you need to run large exports or long maintenance queries by hand, it helps to use a client that connects directly and shows the full error chain rather than a generic driver exception. Chat2DB (opens in a new tab) keeps the connection details and query history together, so it is easy to rerun the failing statement with different session settings. To decode other codes you meet along the way, like 1153, 3024, or 4031, use the free MySQL error code lookup tool (opens in a new tab).
If the lost connection is actually a timeout while waiting for row locks, you will see a different error; see MySQL Lock Wait Timeout Exceeded. If the client cannot connect at all, see MySQL Error 2002: Can't Connect Through Socket.
FAQ
Is error 2013 a server error or a client error?
It is a client error. Codes from 2000 to 2999 are generated by the client library when something goes wrong with the connection, which is why the message does not include a reason. The server-side reason, if the server knew one, is in the error log as an "Aborted connection" note.
Should I just increase all the timeouts?
No. Raising wait_timeout, net_read_timeout, and net_write_timeout hides some symptoms but does nothing for crashes, killed threads, packet size limits, or proxy timeouts, and a large wait_timeout keeps abandoned connections alive. Identify the cause from the error log first, then change the one setting that matches.
Why does my GUI client lose the connection on long queries?
GUI clients have their own read timeout for queries, independent of the server. When a query takes longer than that client-side limit, the client closes the connection and reports 2013. In MySQL Workbench this is the DBMS connection read timeout in the SQL Editor preferences. Raise the client query timeout, or run long statements from the command line.
What is the difference between error 2013 and error 4031?
Both mean the connection is closed. Error 4031, available since MySQL 8.0.24, is sent by the server specifically when it closes an idle connection because of wait_timeout or interactive_timeout. Error 2013 is the generic client-side message used when the client cannot tell why the connection broke.
