Part of the guide: MySQL Errors & Fixes for PHP and Laravel Developers
MySQL Server Has Gone Away (2006) in Laravel & XAMPP: Fix
Your Laravel application is running fine. You open a page, import some data, or execute a background job. Suddenly, the database connection fails.
The error looks like this:
SQLSTATE[HY000]: General error: 2006 MySQL server has gone away
Sometimes you see a related error:
SQLSTATE[HY000]: General error: 2013 Lost connection to MySQL server during query
If you have encountered either message, the first thing to understand is that your database has not necessarily crashed.
MySQL server has gone away (2006) means your application tried to communicate through a database connection that was no longer usable.
The server might have closed an idle connection. A large SQL statement might have exceeded the allowed packet size. The database service might have restarted while Laravel was still processing a request.
These situations produce similar errors, but they need different fixes.
Quick fix: Start by checking wait_timeout, max_allowed_packet, and the database server's uptime. If the problem happens after a long period of inactivity, investigate connection expiration. If it happens during a large import or INSERT statement, inspect the packet limit. If several requests fail together, read the database error log.
This guide walks through the diagnosis and fixes for Laravel, Windows XAMPP, MySQL, MariaDB, and cPanel hosting.
1. What Causes MySQL Server Has Gone Away (2006)?
The two common lost-connection errors come from the MySQL client library.
| Error | MySQL identifier | What happened |
|---|---|---|
| 2006 | CR_SERVER_GONE_ERROR | The client could not successfully send a query to the server |
| 2013 | CR_SERVER_LOST | The client did not receive a complete response from the server |
The operating system and client library can affect which error appears.
Both errors can indicate that the connection was interrupted, but they do not identify the underlying cause by themselves.
Cause 1: The database connection timed out
MySQL uses wait_timeout to control how long an inactive, non-interactive connection can remain open.
A commonly documented default is:
wait_timeout = 28800
That is eight hours.
A hosting provider may configure a much shorter timeout.
Imagine an Artisan command that connects to MySQL, downloads attendance records from another server, processes the files, and then attempts another SQL query.
If the connection remains idle longer than the configured timeout, MySQL may close it.
The next query can return error 2006.
One detail matters here: wait_timeout is an idle-connection timeout. It does not limit how long an actively running SQL query may execute.
Cause 2: max_allowed_packet is too small
MySQL uses max_allowed_packet to limit communication packet sizes.
A single large SQL statement may exceed that setting.
For example:
INSERT INTO articles (title, body)
VALUES
('First article', 'Large HTML content'),
('Second article', 'Another large HTML document');
This simple example will not normally cause a problem.
But imagine inserting thousands of articles with substantial HTML content in one statement.
The resulting SQL request may exceed the permitted packet size.
When a packet is too large, the database can reject it and close the connection.
This is why a small import may work while a larger import consistently fails.
Cause 3: The database restarted
If MySQL stops or restarts, its existing connections are terminated.
Laravel might still have a reference to an old connection.
When it sends the next query, the connection is no longer usable.
Possible reasons for a restart include scheduled maintenance, a service crash, memory exhaustion, or an administrator restarting the database.
Do not assume a crash occurred without checking the server log.
Cause 4: The connection was killed
A database administrator can terminate a connection using the KILL statement.
Some hosting environments also terminate connections or queries when resource limits are exceeded.
The application may report the resulting interruption as a lost connection.
Cause 5: Network or communication problems
A remote database connection can fail even when the database service is healthy.
Possible causes include unstable networking, firewall resets, database proxy timeouts, or an interrupted transfer.
If Laravel and MySQL run on the same Windows XAMPP installation, remote-network problems are generally less likely.
If your Laravel application connects to a database hosted elsewhere, investigate networking as well.
2. Check the Database Settings Before Changing Anything
I would start with the current database settings rather than immediately increasing timeouts.
You can run these checks in phpMyAdmin or a MySQL command-line client.
Step 1: Identify your database version
Open phpMyAdmin, select the SQL tab, and execute:
SELECT VERSION() AS server_version;
This tells you which database server you are connected to.
The distinction matters because XAMPP commonly uses MariaDB rather than Oracle MySQL.
For example, the Windows XAMPP 8.2.12 release included MariaDB 10.4.32.
If your installation has been upgraded or replaced, the version may be different.
Step 2: Check the timeout and packet limits
Run:
SHOW VARIABLES LIKE 'wait_timeout';
SHOW VARIABLES LIKE 'interactive_timeout';
SHOW VARIABLES LIKE 'max_allowed_packet';
You can compare the current session and global settings with:
SELECT
@@SESSION.wait_timeout AS session_wait_timeout,
@@GLOBAL.wait_timeout AS global_wait_timeout,
@@SESSION.max_allowed_packet AS session_packet_limit,
@@GLOBAL.max_allowed_packet AS global_packet_limit;
This comparison is useful because existing sessions do not necessarily receive every global setting change immediately.
The variables mean:
| Variable | Purpose |
|---|---|
wait_timeout | Controls inactive non-interactive connections |
interactive_timeout | Controls the initial idle timeout for interactive clients |
max_allowed_packet | Limits communication packet size |
net_read_timeout | Controls how long the server waits for incoming data |
net_write_timeout | Controls how long the server waits while sending data |
The packet value is reported in bytes.
For reference:
| Value | Size |
|---|---|
| 1048576 | 1 MiB |
| 16777216 | 16 MiB |
| 67108864 | 64 MiB |
If you see 16777216, the packet limit is 16 MiB.
That does not automatically mean the value is too small. You need to compare it with the size of the failing statement.
Step 3: Check the database uptime
Run:
SHOW GLOBAL STATUS LIKE 'Uptime';
The result shows how many seconds the database server has been running.
Compare it with the time of the error.
A recent restart can help explain why previously established connections stopped working.
However, uptime alone does not tell you whether the database crashed.
The service could have restarted normally, or someone may have restarted it after the original error.
Use the error log to establish what happened.
Step 4: Find the database error log
Run:
SHOW VARIABLES LIKE 'log_error';
On a typical Windows XAMPP installation, the database logs are located under:
C:\xampp\mysql\data\
The exact filename depends on the configuration.
Check the entries recorded around the time of the failure.
Look for unexpected shutdowns, startup messages, oversized-packet errors, or resource problems.
If you are on shared hosting and cannot access the database log, ask your provider to inspect it.
Step 5: Match the symptom to the cause
| What happens | Possible cause | What to try |
|---|---|---|
| Error after a long pause | Idle connection expired | Reconnect before the next query |
| Error during a large INSERT | Packet limit exceeded | Reduce batch size or adjust packet limit |
| Error during SQL import | Oversized statement or connection failure | Inspect the failing statement |
| Error while reading many rows | Transfer, query, or network problem | Process results in smaller batches |
| Many pages fail together | Database restart or outage | Check database status and error log |
| Error inside a transaction | Connection was lost during the transaction | Review transaction recovery |
| Error on a remote database | Network or proxy interruption | Check network stability and timeouts |
Avoid applying several unrelated fixes at once.
If you change the packet limit, timeout values, and Laravel code together, it becomes harder to determine which change actually solved the problem.
3. Fix max_allowed_packet in Windows XAMPP
If a large SQL statement is causing the problem, the next step is to inspect XAMPP's packet limit.
The configuration file is commonly located at:
C:\xampp\mysql\bin\my.ini
Use the actual installation path if you installed XAMPP elsewhere.
Step 1: Back up my.ini
Before editing the file, create a backup.
Open Command Prompt and run:
copy "C:\xampp\mysql\bin\my.ini" "C:\xampp\mysql\bin\my.ini.bak"
This gives you a copy to restore if the database fails to start after the change.
Step 2: Stop the database service
Open the XAMPP Control Panel.
Find the MySQL row and click Stop.
Wait until the service has stopped.
The panel may say MySQL even when the installed database engine is MariaDB.
Step 3: Update the correct setting
Open the active my.ini file.
Find the [mysqld] section.
Look for an existing max_allowed_packet entry.
For example:
[mysqld]
max_allowed_packet=64M
If the setting already exists, change the existing value rather than adding a duplicate.
This example sets the packet limit to 64 MiB.
It is a reasonable test value when the current limit is insufficient, but it is not required for every Laravel application.
Avoid setting it to an extremely large number unless you know why the application needs it.
Step 4: Understand the client settings
The same file may contain sections such as:
[mysql]
max_allowed_packet=64M
[mysqldump]
max_allowed_packet=64M
These settings apply to the respective command-line programs.
They do not replace the server setting under [mysqld].
Also, adding a setting under [mysql] does not automatically change how phpMyAdmin or PHP's database driver handles every request.
Different database clients may use their own limits.
Step 5: Start MySQL again
Save the configuration file.
Return to the XAMPP Control Panel and start MySQL.
If the database refuses to start, restore your previous configuration and read the error log.
Do not delete database files to fix a configuration error.
For more detailed recovery steps, see MySQL Shutdown Unexpectedly in XAMPP.
Step 6: Verify that the setting changed
Open a new database connection and run:
SHOW GLOBAL VARIABLES LIKE 'max_allowed_packet';
For the example configuration, the expected value is:
67108864
If the value is unchanged, confirm that you edited the configuration file used by the running database service.
Can you change the packet limit without restarting?
On supported MySQL and MariaDB versions, an administrator with sufficient privileges can run:
SET GLOBAL max_allowed_packet = 67108864;
This changes the global value at runtime.
Existing connections may retain their previous session value.
The change is also not necessarily persistent across service restarts.
For a lasting change, update the appropriate server configuration.
Remember that increasing max_allowed_packet only helps when packet size is actually the problem.
It will not repair an expired connection, an unstable network, or a crashing database server.
4. Fix Large SQL Imports in phpMyAdmin and Command Prompt
Large database imports are one of the most common situations where developers encounter error 2006.
But the total SQL file size is not the same as the size of an individual database packet.
A large dump containing thousands of small INSERT statements can import without exceeding the packet limit.
A smaller dump containing one enormous INSERT statement may fail.
Check PHP upload limits first
When you import through phpMyAdmin, PHP and the database server both have limits.
| Setting | What it affects |
|---|---|
upload_max_filesize | Largest uploaded file accepted by PHP |
post_max_size | Maximum HTTP POST request size |
max_execution_time | PHP execution-time limit |
memory_limit | Memory available to the PHP script |
max_allowed_packet | Database communication packet size |
If the SQL file cannot be uploaded at all, check the PHP limits first.
If the upload succeeds but the import fails during execution, look at the SQL statements, packet settings, error logs, and execution environment.
Import a SQL file using Command Prompt
For a large SQL dump, the command-line client avoids phpMyAdmin's PHP upload limits.
Open Windows Command Prompt.
Run:
cd /d C:\xampp\mysql\bin
mysql.exe -u root -p --max_allowed_packet=64M my_database < "C:\path\to\dump.sql"
Replace my_database with your actual database name.
Replace the file path with the location of your SQL dump.
The command does the following:
- Opens the XAMPP MySQL binary directory.
- Starts the MySQL command-line client.
- Connects using the specified database account.
- Sets the client's packet limit for this operation.
- Imports the SQL statements from the file.
The destination database should already exist unless the dump includes statements that create it.
If your local XAMPP root account has no password, pressing Enter at the password prompt may be sufficient.
Never assume that a live database has an empty password.
Run this example in Command Prompt. Standard PowerShell input redirection does not use the same < syntax.
Before importing: Review the SQL dump and confirm the destination database. A dump may contain statements that delete or replace existing data.
Reduce the size of individual INSERT statements
If you control the source database, you can produce a dump without extended INSERT statements.
For example:
cd /d C:\xampp\mysql\bin
mysqldump.exe -u root -p --skip-extended-insert my_database > "C:\path\to\dump.sql"
The --skip-extended-insert option tells mysqldump to avoid combining many rows into one INSERT statement.
The resulting dump may be larger and slower to restore.
However, each statement will typically be smaller.
This can help when multi-row INSERT statements exceed the server's packet limit.
It will not solve a problem caused by one individual row containing a very large binary or text value.
5. Fix Laravel Connections, Queue Workers, and Big Inserts
Laravel already handles some database connection failures.
That does not mean you can ignore them.
The framework can recognize certain lost-connection exceptions and attempt to reconnect, but the behavior depends on the query context.
How Laravel handles a lost connection
Laravel 12 uses lost-connection detection inside its database connection implementation.
If a query fails with a recognized lost-connection error and no transaction is active, Laravel can attempt to reconnect and retry the query.
That may recover from an expired idle connection.
It does not guarantee recovery from every failure.
If the database server is unavailable, reconnecting may fail.
If the SQL statement exceeds max_allowed_packet, executing the same oversized statement again will not help.
There is also a risk with failed write operations.
Suppose the server completes an INSERT but the network connection drops before Laravel receives the response.
The application may not know whether the write succeeded.
Blindly retrying that operation can insert duplicate records.
For important writes, use unique constraints or an idempotent operation design.
Reconnect after a long external operation
Suppose an Artisan command downloads attendance data from a biometric device.
The download takes several minutes, and the database connection remains unused during that time.
Once the data is ready, you can reconnect before saving it.
For example, inside an existing command or service:
use Illuminate\Support\Facades\DB;
// This operation may take several minutes.
$rows = $this->fetchAttendanceFromDevice($branch);
// Refresh the database connection.
DB::reconnect();
if (! empty($rows)) {
DB::table('attendance_logs')->insert($rows);
}
This example assumes $branch and fetchAttendanceFromDevice() are defined in your application.
DB::reconnect() refreshes the default database connection.
If you need a named connection, use:
DB::reconnect('mysql');
Do not reconnect in the middle of an open transaction.
A new connection cannot continue the transaction that belonged to the previous connection.
Why Laravel transactions need special care
Consider this example:
use Illuminate\Support\Facades\DB;
DB::transaction(function () {
DB::table('orders')
->where('id', 1001)
->update(['status' => 'processed']);
DB::table('order_logs')->insert([
'order_id' => 1001,
'message' => 'Order processed',
]);
});
If the connection disappears while the transaction is running, Laravel cannot safely resume the original transaction on a new database connection.
That is why its ordinary lost-connection query retry behavior does not apply in the same way to queries inside active transactions.
For a connection lost close to the COMMIT operation, the application may not know whether the transaction was committed.
That matters for payments, invoices, orders, and other actions that must not be duplicated.
Keep transactions reasonably short and design retries around data consistency rather than simply repeating the entire operation.
Restart Laravel queue workers properly
Laravel queue workers can remain active for long periods.
Because the worker process is reused between jobs, it may retain database connections and application state.
Laravel includes logic to stop a worker after certain unhandled lost-connection failures.
However, the worker needs a process supervisor or another mechanism to restart it.
A typical worker command is:
php artisan queue:work --sleep=3 --max-time=3600 --max-jobs=500
The options mean:
| Option | Purpose |
|---|---|
--sleep=3 | Sleep briefly when no job is available |
--max-time=3600 | Limit the worker's overall running time |
--max-jobs=500 | Exit after processing the configured number of jobs |
These settings can help recycle long-running workers.
They are not a complete fix for database outages.
Also, --max-time does not forcibly terminate every running job the moment the duration is reached. The worker normally checks its exit conditions between processing jobs.
For Linux production servers, Laravel documents using Supervisor to manage queue workers.
On Windows, use a properly configured Windows-compatible process supervisor or service manager.
After a deployment, run:
php artisan queue:restart
This requests a graceful worker restart.
The restart command relies on Laravel's configured cache infrastructure to communicate the restart signal.
Use short-lived workers on shared hosting
If your cPanel hosting provider permits cron jobs but not permanent background processes, a short-lived queue worker may be more practical.
For example:
php artisan queue:work --stop-when-empty
The worker processes available jobs and exits when the queue becomes empty.
You can schedule it using cron if your hosting environment supports that approach.
Use the correct PHP executable and project path.
Also, avoid overlapping cron executions unless your queue processing is designed to handle concurrent workers.
For a busy application with time-sensitive jobs, a continuously supervised worker is usually a better fit.
Split large Laravel INSERT statements
If an import contains thousands of rows, avoid building one enormous INSERT statement.
For example:
use Illuminate\Support\Facades\DB;
foreach (array_chunk($rows, 200) as $chunk) {
DB::table('attendance_logs')->insert($chunk);
}
This code divides the input array into batches of 200 rows.
The number is only a starting point.
A batch containing large text values may need to be smaller.
A batch containing short values may safely contain more rows.
The right size depends on your data and database limits.
Also, array_chunk() does not solve PHP memory problems if $rows already contains millions of records.
For very large imports, read records incrementally instead of loading everything into memory.
Read large tables in smaller batches
Avoid loading an entire large table when you only need to process records one at a time.
Instead of:
$employees = DB::table('employees')->get();
use:
DB::table('employees')
->chunkById(500, function ($employees) {
foreach ($employees as $employee) {
// Process this employee.
}
});
This retrieves records in smaller groups using the table's ID column.
For sequential processing, lazyById() is another option.
These approaches reduce memory usage and avoid transferring a very large result set in one request.
They do not eliminate problems caused by a database crash or an unstable network.
6. Common Problems and Troubleshooting Mistakes
If the error continues after checking the main settings, use the actual failure pattern to narrow the investigation.
Problem: MySQL server has gone away after an idle period
Check:
SHOW VARIABLES LIKE 'wait_timeout';
If the application leaves connections unused for long periods, reconnect before the next database operation.
Do not automatically set wait_timeout to an extremely high value.
Long idle timeouts can leave unused connections occupying server resources.
Problem: Lost connection during a large SELECT
A large query result can place pressure on the database, PHP memory, or the network connection.
First, check whether the query can return fewer rows.
Use pagination or incremental processing where appropriate.
If the problem occurs during result transfer, investigate net_write_timeout, client-side read timeouts, and network stability.
Be careful with net_read_timeout. It controls how long the server waits for incoming data from the client, not the entire duration of a SQL query.
Increasing it is not a universal fix for error 2013.
Problem: Database keeps restarting
Check:
SHOW GLOBAL STATUS LIKE 'Uptime';
Then inspect the error log:
SHOW VARIABLES LIKE 'log_error';
Look for evidence of a crash, startup error, or resource problem.
Do not delete InnoDB files or overwrite the database data directory.
If MySQL will not start, investigate the startup error separately.
Problem: Shared hosting does not allow configuration changes
On ordinary cPanel shared hosting, you generally cannot edit the server's MySQL configuration file.
You may also lack permission to run:
SET GLOBAL max_allowed_packet = 67108864;
That is normal for a restricted hosting account.
You can still reduce INSERT batch sizes, avoid large SQL statements, reconnect in long-running commands, and ask your provider to inspect the database logs.
When requesting support, include the exact error, affected query or operation, timestamp, and current database settings.
Do not send database passwords or your complete Laravel .env file.
Problem: Error returns after restarting MySQL
Restarting the database clears existing connections.
That can temporarily make the application work again.
But if the underlying issue is an oversized statement or an idle worker, the same error can return.
Use the original failing operation as your test.
If the error reappears under the same conditions, focus on that repeatable pattern.
Problem: Too many database connections
Increasing idle timeouts or enabling persistent connections without planning connection capacity can create another problem.
On shared hosting, you may encounter:
User has exceeded the max_user_connections resource
If that happens, review how many database connections the application opens and how they are released.
The MySQL max_user_connections Error on Shared Hosting guide covers that problem separately.
Problem: SQL imports create duplicate records
After a failed import, do not assume that every statement was rolled back.
Some statements may already have completed.
Restarting an import without checking the existing data can create duplicates or overwrite records.
Use a backup, review the dump, and confirm the destination database before retrying.
For application-managed imports, consider unique constraints and a resumable import process.
Quick verification after fixing the error
Once you have made the relevant change, check these results:
- The database server remains running.
- The expected
max_allowed_packetand timeout values are active. - The original failing operation completes.
- Laravel logs contain no new matching lost-connection error for that test.
- Imported records are complete.
- No duplicate records were created.
- Queue jobs complete successfully.
- The database error log does not show another unexpected restart.
Do not rely on a successful homepage load as proof that the database problem is resolved.
If the error originally happened during a large import, repeat that import safely in a test environment.
If it happened during a long-running job, verify the job under the same conditions.
For applications that regularly need queue workers, large imports, or server-level database tuning, see Shared Hosting vs VPS for Laravel.
7. FAQ
What does MySQL server has gone away error 2006 mean?
Error 2006 means the database client could no longer communicate through its existing connection.
It can happen because the connection timed out, the database restarted, a communication packet exceeded the allowed limit, or another connection problem occurred.
Check wait_timeout, max_allowed_packet, server uptime, and the database error log to identify the cause.
What should max_allowed_packet be in XAMPP?
There is no single correct setting for every application.
If your current packet limit is too small for a legitimate SQL statement, 64M can be a reasonable troubleshooting value.
On a typical Windows XAMPP installation, configure it under [mysqld] in the active my.ini file.
Restart the database and verify the value using:
SHOW GLOBAL VARIABLES LIKE 'max_allowed_packet';
If the original error was caused by a timeout rather than an oversized packet, increasing this setting will not help.
Does Laravel reconnect automatically after MySQL server has gone away?
Laravel 12 can detect some lost connections and attempt to reconnect and retry a query when no transaction is active.
It cannot guarantee recovery from every disconnection.
Inside an active transaction, Laravel cannot simply reconnect and continue the original transaction.
Long-running jobs should handle connection failures carefully, and write operations should be designed to prevent duplicate records during retries.
Why does a large SQL import fail in phpMyAdmin?
A large SQL import can fail because of PHP upload limits, execution limits, oversized SQL statements, packet limits, or database connection problems.
If the file cannot upload, check upload_max_filesize and post_max_size.
If the upload succeeds but database execution fails, inspect the failing SQL statement and max_allowed_packet.
Using the MySQL command-line client can avoid PHP upload limits, but the server's database packet limit still applies.
Final check
When MySQL reports that the server has gone away, do not start by increasing every timeout or restarting services repeatedly.
Find out whether the connection expired, the request was too large, or the database service actually stopped.
Then make the smallest change that addresses that cause and repeat the original failing operation.
That approach is more reliable than a configuration change that only hides the problem until it happens again.
Sources
- MySQL 8.4: MySQL Server Has Gone Away
https://dev.mysql.com/doc/refman/8.4/en/gone-away.html
- MySQL 8.4: Packet Too Large
https://dev.mysql.com/doc/refman/8.4/en/packet-too-large.html
- MySQL 8.4: Server System Variables
https://dev.mysql.com/doc/refman/8.4/en/server-system-variables.html
- MySQL 8.4: mysqldump
https://dev.mysql.com/doc/refman/8.4/en/mysqldump.html
- MariaDB: Server System Variables
https://mariadb.com/docs/server/server-management/variables-and-modes/server-system-variables
- Laravel 12: Queue Documentation
https://laravel.com/docs/12.x/queues
- Laravel 12: Database Connection Source
https://github.com/laravel/framework/blob/12.x/src/Illuminate/Database/Connection.php
- Laravel 12: Queue Worker Source
https://github.com/laravel/framework/blob/12.x/src/Illuminate/Queue/Worker.php
- Apache Friends: XAMPP 8.2.12 Release
https://www.apachefriends.org/blog/new_xampp_20231119.html


