"MySQL server has gone away" in WordPress

This error means WordPress had a working database connection, but MySQL or MariaDB closed it before the current query could finish. The most common triggers are an idle connection that exceeded wait_timeout, or a query packet larger than max_allowed_packet.

Your error log, WP-CLI output, or a plugin's admin notice shows a line like this:

WordPress database error MySQL server has gone away for query [...]

Sometimes the visible symptom is different: a WooCommerce CSV import that silently stops at row 4,000, a migration plugin that reports "database error" halfway through, or a WP-Cron job that processes half a queue and then dies. The underlying MySQL client error is always the same: error 2006 (CR_SERVER_GONE_ERROR).

What this error actually means

Error 2006 means the TCP connection between PHP and MySQL was alive at some point during the current request, but MySQL closed it before PHP could send or receive the next query. The MySQL reference manual lists 14 documented causes, but in WordPress the trigger is almost always one of two things: the connection sat idle too long, or the query packet was too large.

This is a different error from Error establishing a database connection. That error appears when WordPress cannot open a connection at all (wrong credentials, MySQL down, max_connections hit). "MySQL server has gone away" appears after the connection was successfully established and then dropped during the same PHP process. The database itself is typically running fine.

Common causes, ordered by likelihood

  1. wait_timeout expired. MySQL closes any connection that has been idle for longer than wait_timeout, which defaults to 28,800 seconds (8 hours) on both MySQL 8.0 and MariaDB. That sounds generous, but shared hosts routinely lower it to 60–300 seconds to reclaim connection slots. A long-running PHP script that does heavy computation between database calls (image processing, XML parsing, HTTP API calls to external services) can easily exceed a lowered wait_timeout.

  2. Query packet exceeds max_allowed_packet. When a single SQL statement is larger than max_allowed_packet, MySQL closes the connection and returns error 2006. The default was 4 MB in MySQL 5.7 and was raised to 64 MB in MySQL 8.0. WooCommerce product imports with large serialized meta_value fields, or bulk INSERT statements generated by migration plugins, can exceed this limit on older MySQL versions or on hosts that still set a conservative value.

  3. MySQL server actually restarted. Rare, but it happens. A hosting provider rotates the database server, an OOM killer terminates mysqld, or a configuration change triggers a restart. Any connection established before the restart is invalid afterward.

  4. PHP 7.3 or earlier connecting to MySQL 8.0. MySQL 8.0 uses caching_sha2_password as the default authentication plugin. PHP versions below 7.4 lack native support for this plugin. The result is error 2006 on the very first query, even though the connection handshake appeared to succeed. This one is identifiable because it happens immediately, not after a delay.

Diagnose before you fix

Run these checks to identify which cause you are dealing with. None of them modify the database.

Check the current timeout and packet size

Connect to MySQL from the command line or through phpMyAdmin and run:

SHOW VARIABLES LIKE 'wait_timeout';
SHOW VARIABLES LIKE 'max_allowed_packet';

Expected output on a default MySQL 8.0 installation:

+---------------+-------+
| Variable_name | Value |
+---------------+-------+
| wait_timeout  | 28800 |
+---------------+-------+

+--------------------+----------+
| Variable_name      | Value    |
+--------------------+----------+
| max_allowed_packet | 67108864 |
+--------------------+----------+

If wait_timeout is below 300, or max_allowed_packet is below 16777216 (16 MB), you have a likely suspect.

Check the error log for timing clues

Enable the WordPress debug log if it is not already active. Open wp-config.php through your hosting panel's file manager (or via SFTP) and add these lines (or verify they are set):

define('WP_DEBUG', true);
define('WP_DEBUG_LOG', true);    // writes to wp-content/debug.log
define('WP_DEBUG_DISPLAY', false);

Reproduce the failing operation and check wp-content/debug.log. The WordPress debug log article covers this setup in more detail. If the error appears after several minutes of processing, wait_timeout is the cause. If it appears on the first large batch insert, max_allowed_packet is more likely.

Check PHP and MySQL versions

In wp-admin, go to Tools > Site Health > Info > Server. The PHP version and database server version are both listed there. Alternatively, your hosting panel's PHP version selector shows the active PHP version, and phpMyAdmin shows the MySQL or MariaDB version in its start screen.

If you have SSH access:

php -v
mysql --version

If you see PHP 7.3 or lower paired with MySQL 8.0, the authentication plugin mismatch is the most probable cause.

Fix 1: raise max_allowed_packet for large queries

This fix targets cause 2: the query or data packet is too large for the current limit.

Many hosting panels expose a MySQL configuration screen or a support option to raise max_allowed_packet. Check your hosting panel's database settings first. If your host does not expose it, you can set the value per session by creating a must-use plugin. Using your hosting panel's file manager or an SFTP client, create the file wp-content/mu-plugins/raise-packet-size.php:

<?php
// Raise max_allowed_packet for this MySQL session only.
// Safe to run on every request; the SET only affects the current connection.
global $wpdb;
$wpdb->query("SET SESSION max_allowed_packet = 128 * 1024 * 1024");

If you have SSH access and can edit the MySQL server configuration, add or update the value under the [mysqld] section of my.cnf (or my.ini on Windows):

[mysqld]
max_allowed_packet = 128M

Then restart MySQL:

# systemd on Debian/Ubuntu
sudo systemctl restart mysql
# or MariaDB
sudo systemctl restart mariadb

You will know it worked when the import or migration completes without the "gone away" error and SHOW VARIABLES LIKE 'max_allowed_packet' in phpMyAdmin returns the new value.

Fix 2: raise wait_timeout for long-running scripts

This fix targets cause 1: the connection sits idle too long between queries.

Some hosting panels let you adjust wait_timeout through database settings or a support request. If that is not available, you can set it per session from within WordPress. Add a must-use plugin at wp-content/mu-plugins/raise-wait-timeout.php via your hosting panel's file manager or SFTP:

<?php
// Raise wait_timeout for this MySQL session only.
global $wpdb;
$wpdb->query("SET SESSION wait_timeout = 3600");

You can also run this as a one-off query in phpMyAdmin before starting a long import:

SET SESSION wait_timeout = 3600;

WP-CLI alternative:

wp db query "SET SESSION wait_timeout = 3600"

If you have SSH access and can edit MySQL server configuration, add to my.cnf under [mysqld]:

[mysqld]
wait_timeout = 3600

Keep the value reasonable. Setting wait_timeout to 86400 (24 hours) on a server with 151 max_connections means 151 idle connections can survive for a full day, eating connection slots that active requests need. A value between 1,800 and 3,600 seconds covers most long-running WordPress tasks without hoarding connections.

You will know it worked when the long-running task completes without error 2006. If the task runs for more than an hour, you should also consider breaking it into smaller batches (see the prevention section below).

Fix 3: resolve the PHP/MySQL authentication mismatch

This fix targets cause 4: PHP < 7.4 connecting to MySQL 8.0 with caching_sha2_password.

The right fix is to upgrade PHP to at least 7.4, which adds native support for caching_sha2_password. Most hosting panels have a PHP version selector where you can switch with a single click. PHP 7.3 reached end of life in December 2021 and receives no security patches.

If upgrading PHP is not immediately possible, you can switch the MySQL user's authentication plugin back to mysql_native_password:

ALTER USER 'wp_user'@'localhost'
  IDENTIFIED WITH mysql_native_password BY 'your-password-here';
FLUSH PRIVILEGES;

This is a stopgap. mysql_native_password is deprecated in MySQL 8.0 and may be removed in a future release.

You will know it worked when error 2006 stops appearing on the first query, and the connection stays alive for subsequent queries.

How WordPress handles this error internally

Since WordPress 3.9, the wpdb::check_connection() method detects error 2006, attempts to reconnect up to 5 times with 1-second intervals, and retries the failed query once. This means transient drops (a MySQL restart that takes 2–3 seconds) are often recovered silently.

The retry budget is limited: 5 attempts, each 1 second apart. If MySQL is still unreachable after 5 seconds, WordPress gives up and either returns false (during front-end rendering after template_redirect) or calls dead_db() and terminates. For background tasks like WP-Cron or WP-CLI, the reconnect logic works the same way, but the task itself does not retry from the point of failure. The half-completed import stays half-completed.

When to escalate

If raising wait_timeout and max_allowed_packet does not resolve the error, the cause may be infrastructure-level (firewall dropping idle TCP connections, MySQL process crashing, OOM killer). Before opening a ticket with your host or with me, collect:

  • The exact error line from wp-content/debug.log or the PHP error log
  • Output of SHOW VARIABLES LIKE 'wait_timeout' and SHOW VARIABLES LIKE 'max_allowed_packet'
  • Your WordPress, PHP, and MySQL/MariaDB versions
  • The operation that triggers the error (import, migration, WP-CLI command, cron job)
  • How long the operation runs before failing (30 seconds? 10 minutes? 2 hours?)
  • Whether the error happens on every attempt or intermittently
  • The last 50 lines of /var/log/mysql/error.log (or the equivalent in your hosting panel)

This information separates a productive support conversation from guesswork.

Prevent recurrence

  • Break large operations into batches. A WooCommerce import of 50,000 products should run in chunks of 500–1,000 rows with explicit commits between batches. Most import plugins (WP All Import, WooCommerce's built-in importer) have a batch-size setting. Use it.
  • Avoid idle gaps in long scripts. If your script makes HTTP calls, processes images, or waits for external APIs between database queries, the connection can time out. Run $wpdb->check_connection() before the next query after any long non-database operation, or set a generous session-level wait_timeout.
  • Pin max_allowed_packet above 64 MB on MySQL 5.7. The 4 MB default on MySQL 5.7 is too small for most WordPress sites with WooCommerce or custom post types that store serialized data. MySQL 8.0 defaults to 64 MB, but if you are still on 5.7, set it explicitly.
  • Monitor Aborted_clients. The MySQL status variable Aborted_clients increments every time a connection is closed because the client did not close it properly (which includes timeout closures). A rising count tells you the problem is recurring even when no one is watching.
SHOW GLOBAL STATUS LIKE 'Aborted_clients';

If you see an error that says maximum execution time exceeded, that is a different problem: PHP itself is killing the script, not MySQL closing the connection. The two often appear during the same kind of long-running task, but the fixes are completely different.

Want this to stop being your problem?

If outages or errors keep repeating, the fix is often consistency: updates, backups and monitoring that don't get skipped.

Compare maintenance plans

Search this site

Start typing to search, or browse the knowledge base and blog.