MySQL "Lost Connection to MySQL Server During Query" (2013) and "MySQL Server Has Gone Away" (2006): Every Cause and Fix, From Lambda to RDS
"Lost connection to MySQL server during query" (ERROR 2013) and "MySQL server has gone away" (ERROR 2006) both mean the connection between your program and MySQL closed. The difference is timing: 2013 means it closed while a query was waiting for its answer, and 2006 means your program kept using a connection that was already closed. The causes are a short list: the connection sat idle longer than the server's wait_timeout (8 hours by default), a statement was larger than max_allowed_packet (64 MB), the client gave up waiting for a long query, or the server killed, restarted or failed over. The one that catches AWS users most is the first, in disguise: a Lambda function keeps its database connection between invocations, and when it sits idle past the timeout, the next invocation fails, even though nothing changed. The fix is to check the connection before using it and reconnect, which is one line in most drivers. This page decodes what each client actually prints (MySQL 8.4 adds a clearer ERROR 4031), and fixes every cause, with tested code for Lambda.
Ethan, a developer, runs a small Lambda function that checks repair statuses in the RDS database behind his friend Jake's phone repair shop. It worked in every test. In production, the first status check every morning failed with "Lost connection to MySQL server during query", and every check after it worked.
New to these services? Amazon RDS runs MySQL for you, and AWS Lambda runs your code on demand. A database connection is the open line between them; most of this page is about what closes it.
2013, 2006 or 4031: what each one is telling you
The same dropped connection shows up under different numbers depending on the client and on when you notice. These are real messages, from one MySQL 8.4 server closing an idle connection:
| Error | Message | What it means |
|---|---|---|
| 4031 | The client was disconnected by the server because of inactivity | MySQL 8 told the client why it closed the connection; current clients show it |
| 2013 | Lost connection to MySQL server during query | The connection closed while a query was waiting for its answer |
| 2006 | MySQL server has gone away | The program sent another query on a connection already known to be closed |
| 1153 | Got a packet bigger than 'max_allowed_packet' bytes | One statement or row was too large for the server |
Which of these you see depends on your client. Here's the same idle disconnect, as each one reports it:
| Your client | What you see |
|---|---|
| MySQL 8.4 command-line client | ERROR 4031 |
| MySQL 5.7 command-line client | ERROR 2013 on the next statement, then 2006 on each one after it |
| Python, PyMySQL | OperationalError (2013, 'Lost connection to MySQL server during query') |
| Python, mysqlclient | OperationalError (4031, ...), then (2013, 'Lost connection to server during query') |
| Node.js, mysql2 | A connection error with code 4031, then "Can't add new command when connection is in closed state" |
Different words, one cause. If your log shows any of them, read on for which situation produced it.
The most common cause: idle connections and wait_timeout
MySQL closes any connection that has been idle longer than wait_timeout. Check the server's settings:
SELECT @@wait_timeout, @@interactive_timeout, @@max_allowed_packet, @@net_read_timeout, @@net_write_timeout;
+----------------+-----------------------+----------------------+--------------------+---------------------+
| @@wait_timeout | @@interactive_timeout | @@max_allowed_packet | @@net_read_timeout | @@net_write_timeout |
+----------------+-----------------------+----------------------+--------------------+---------------------+
| 28800 | 28800 | 67108864 | 30 | 60 |
+----------------+-----------------------+----------------------+--------------------+---------------------+
28800 seconds is 8 hours, and 67108864 bytes is 64 MB; those are MySQL 8.4's defaults. To watch the timeout happen without waiting 8 hours, shorten it for one session and pause between two statements:
(echo "SET SESSION wait_timeout = 5; SELECT 'first query';"; sleep 8; echo "SELECT 'after 8 idle seconds';") | mysql -h 127.0.0.1 -u app -p
ERROR 4031 (HY000) at line 2: The client was disconnected by the server because of inactivity. See wait_timeout and interactive_timeout for configuring this behavior.
Raising wait_timeout only delays this. If your program keeps a connection open between bursts of work, as a web app, a scheduled job or a Lambda function does, it will eventually leave one idle for longer than any timeout you set. The lasting fix is in your code: check a connection before you use it, and reconnect if it's gone.
Lambda: the connection that sleeps between invocations
Lambda keeps your function's process around between invocations, including anything created outside the handler, which is why examples open the database connection there: it saves the time of reconnecting on every call. The catch is that the process is frozen between invocations, but the database's clock keeps running. Here's Ethan's original function:
import os
import pymysql
conn = pymysql.connect(host=os.environ["DB_HOST"], port=int(os.environ.get("DB_PORT", "3306")),
user=os.environ["DB_USER"], password=os.environ["DB_PASSWORD"], database="shop")
def handler(event, context):
with conn.cursor() as cur:
cur.execute("SELECT COUNT(*) FROM photos")
(count,) = cur.fetchone()
return {"photos": count}
The first invocation works. An invocation after the connection has sat idle past the server's timeout fails:
{"errorMessage": "(2013, 'Lost connection to MySQL server during query')", "errorType": "OperationalError"}
That's the "first check every morning fails" pattern: overnight, nobody called the function, the connection timed out, and the first call of the day found it dead. One line fixes it. PyMySQL's ping(reconnect=True) checks the connection and reopens it if the server closed it:
import os
import pymysql
conn = pymysql.connect(host=os.environ["DB_HOST"], port=int(os.environ.get("DB_PORT", "3306")),
user=os.environ["DB_USER"], password=os.environ["DB_PASSWORD"], database="shop")
def handler(event, context):
conn.ping(reconnect=True) # reopens the connection if the server closed it while the function was idle
with conn.cursor() as cur:
cur.execute("SELECT COUNT(*) FROM photos")
(count,) = cur.fetchone()
return {"photos": count}
With that line, the same idle gap passes without an error. For many functions sharing one database, Amazon RDS Proxy keeps a pool of connections on its side and hands them out to invocations, which also protects the database from a burst of new connections; our Lambda and RDS Proxy guide covers it.
Jake: "So the function fell asleep holding the phone, and the database hung up?"
Ethan: "Exactly. Now it checks for a dial tone before it talks."
Apps with connection pools
If your web app uses a connection pool, a set of open connections that your requests borrow and return, the same thing happens inside it. A pooled connection that sits unused past wait_timeout is closed on the server's side, and the request that borrows it next gets the error. Two pool settings prevent that, under different names in each library:
- Test before use: the pool checks a connection, usually with a ping, before handing it out, and replaces a dead one.
- Retire before the timeout: the pool closes connections older or idler than a limit you set, comfortably below
wait_timeout. - Retry once on a connection error: for read-only queries, catching a 2013 or 2006 and retrying on a fresh connection covers restarts and failovers too.
- Close what you don't need: scripts and jobs that run once should open a connection, do the work and close it, rather than hold one.
Check your library's documentation for the exact option names; many pools have both of the first two.
ERROR 1153 and max_allowed_packet: one statement too big
A single statement or row larger than the server's max_allowed_packet is refused, and the server closes the connection:
ERROR 1153 (08S01) at line 1: Got a packet bigger than 'max_allowed_packet' bytes
This is the classic cause during imports, when a dump file has huge multi-row INSERT statements or large images stored in BLOB columns. A 70 MB value against the 64 MB default is refused even when the client allows 1 GB; the server's limit is the one that counts. Two changes, together:
- Raise the server's
max_allowed_packet. On a server you run,SET GLOBAL max_allowed_packet = 268435456;sets 256 MB for new connections; on RDS, set it in a custom DB parameter group. - Raise the client's limit for tools that have their own, such as
mysql --max-allowed-packet=256Mormysqldump --max-allowed-packet=256M.
For files and images, storing them in S3 and keeping only the key in the database avoids the limit entirely, and keeps backups small.
Long queries and client read timeouts
Sometimes the server is fine and your client gives up. Most drivers and GUI tools have a read timeout: how long they wait for a query's answer before dropping the connection. A query that runs longer than that fails with 2013, even though the server would have finished. With PyMySQL's read_timeout=3 and a query that takes 10 seconds:
OperationalError (2013, 'Lost connection to MySQL server during query (The read operation timed out)')
The "(The read operation timed out)" part is the clue that the client, not the server, ended it. Either raise the client's read timeout for that job, or make the query faster, usually with an index. GUI tools have the same kind of setting in their connection or SQL editor preferences, and a long report run from a desktop tool is a common way to hit it.
Killed, restarted or failed over
Any event that ends connections on the server side shows up as 2013 on the next query, and 2006 after that. That includes an administrator's KILL (or CALL mysql.rds_kill(...) on RDS), a server restart, an RDS reboot or maintenance, and a Multi-AZ failover, where the database moves to a standby and every existing connection breaks. After a KILL, the program's next query reports:
ERROR 2013 (HY000) at line 2: Lost connection to MySQL server during query
You can't prevent failovers, so your application must survive them: reconnect on these errors, and retry the work if it's safe to repeat. RDS events in the console, or in CloudWatch, show when a reboot or failover happened, which tells you whether a burst of 2013 errors had a cause outside your code.
Jake: "So even when AWS swaps the database underneath us, the app just has to dial again?"
Ethan: "Dial again, and repeat the last step if it's safe to. That's all a failover asks of us."
Find your cause in five minutes
Work through it in this order:
- Note the exact number: 4031 or a 2013 after a long gap points to idle timeouts; 1153 to packet size; 2013 with "read operation timed out" to the client's timeout.
- Check when it happens: first request after a quiet period means idle connections; during imports means packet size; on long reports means read timeouts.
- Check the server's settings with the
SELECT @@wait_timeout ...query above. - Look for restarts, reboots or failovers at the same time in the RDS events.
- Fix in the program first, with ping and reconnect, pool settings or batch sizes, and raise server limits only where a real workload needs it.
Troubleshooting by symptom
First request after a quiet period fails, then everything works
An idle connection passed wait_timeout. Ping and reconnect before use, or configure the pool to test connections.
ERROR 2013 in Lambda on some invocations
The function reuses a connection that timed out between invocations. Call ping(reconnect=True) in the handler.
ERROR 1153 Got a packet bigger than max_allowed_packet
A statement is larger than the server allows. Raise max_allowed_packet on the server and in the client.
2013 Lost connection ... (The read operation timed out)
The client's read timeout ended a slow query. Raise the timeout for that job, or speed up the query.
Can't add new command when connection is in closed state
Node.js mysql2 is using a closed connection. Use a pool, and handle connection errors by reconnecting.
Many 2013 errors at the same moment
The server restarted, rebooted or failed over. Check RDS events, and make the app reconnect and retry.
MySQL lost connection and server gone away: frequently asked questions
What does Lost connection to MySQL server during query mean?
The connection closed while a query was waiting for its result: idle timeout, a killed connection, a restart or a client timeout.
What does MySQL server has gone away mean?
The program sent a query on a connection that had already closed. Reconnect before using it again.
What is the difference between error 2013 and 2006?
2013 is reported for the query during which the connection was lost; 2006 for queries sent after that on the same dead connection.
What is ERROR 4031 in MySQL?
MySQL 8's message for a connection closed after being idle longer than wait_timeout or interactive_timeout.
What is the default wait_timeout in MySQL?
28800 seconds, which is 8 hours. Check it with SELECT @@wait_timeout.
Should I increase wait_timeout to fix the error?
Only as a short-term measure. Any idle connection eventually passes it; reconnecting in the program is the real fix.
How do I fix MySQL lost connection errors in AWS Lambda?
Call conn.ping(reconnect=True) at the start of the handler, or use RDS Proxy for many concurrent functions.
What is the default max_allowed_packet in MySQL 8?
64 MB (67108864 bytes) on MySQL 8.4. Raise it for large rows or big multi-row inserts.
How do I change max_allowed_packet on Amazon RDS?
Set it in a custom DB parameter group attached to the instance; SET GLOBAL isn't allowed for the master user.
Why does my MySQL import fail with server has gone away?
Usually a statement larger than max_allowed_packet. Raise it on the server and pass --max-allowed-packet to the client.
Why do long queries fail with Lost connection to MySQL server?
The client's read timeout expired before the query finished. Raise that timeout or make the query faster.
Does an RDS failover cause lost connection errors?
Yes. Every existing connection breaks during a failover, so applications should reconnect and retry.
How do I keep a MySQL connection alive?
Don't rely on keeping it alive. Test connections before use, or let a pool replace idle ones before wait_timeout.
What does Can't add new command when connection is in closed state mean?
A Node.js mysql2 connection was closed, often by an idle timeout, and the code tried to use it again.
Can I reproduce wait_timeout errors without waiting 8 hours?
Yes. Run SET SESSION wait_timeout = 5 and pause more than 5 seconds before the next query.
Do these errors mean the database is down?
Usually not. They mean one connection closed; the server typically accepts new connections immediately.
Ethan added the ping(reconnect=True) line to the status function and deployed it before closing time. The next morning, the first status check of the day went through like every other one. Jake never knew there had been a problem, which, Ethan pointed out, is the whole point.
📌 If you keep one line from this page
2013 and 2006 mean a closed connection: reconnect before using long-lived connections (ping in Lambda), raise max_allowed_packet for big statements, and raise client timeouts only for slow queries.
4031 = idle · 1153 = too big · "read operation timed out" = client · many at once = restart or failover.
Revision note. Written October 13, 2026, for MySQL 8.4 and Amazon RDS for MySQL, with Lambda on Python 3.13. "Strike while the iron is hot," says the proverb; with database connections, check the iron is still hot before you strike.