Skip to content
Helm logo Helm Download

Fix “MySQL shutdown unexpectedly” in XAMPP —
and stop it coming back

Updated · Windows 10 and 11

The short answer

This error almost never means your data is gone. It means InnoDB was killed mid-write, its redo log no longer matches its data files, and it is refusing to start rather than risk making things worse.

To recover now: stop XAMPP, rename xampp\mysql\data to data_old, copy xampp\mysql\backup to a new data folder, then copy your own database folders and ibdata1 from data_old into it. Full steps below. To stop it happening again: run MySQL as a Windows service, not as a process XAMPP kills. Why that works.

What the error actually is

The message XAMPP shows you is not MySQL's message. It is XAMPP's summary of what it observed: it launched mysqld.exe, the process exited on its own, and XAMPP does not know why. The full text is usually some version of this:

Error: MySQL shutdown unexpectedly.
This may be due to a blocked port, missing dependencies,
improper privileges, a crash, or a shutdown by another method.

That list is XAMPP guessing. The real reason is written down in a file XAMPP does not show you: xampp\mysql\data\mysql_error.log. Open it and read the last twenty lines before you touch anything. If you see phrases like InnoDB: Database page corruption, Log scan progressed past the checkpoint lsn, or Plugin 'InnoDB' init function returned error, you have the corruption case this page is about.

If instead you see Can't start server: Bind on TCP/IP port, you have a completely different and much easier problem: something else is already on port 3306. That is usually a MySQL service left behind by a previous install, or Windows' own MySQL Installer. Stop that service and start XAMPP again.

Why InnoDB ends up in this state

InnoDB does not write your changes straight into the table files. When you insert a row, it writes the change into an in-memory buffer pool and appends a record to a redo log (ib_logfile0). The actual data file (ibdata1 and the .ibd files) gets updated later, in batches, because writing every change immediately would be brutally slow.

That design is safe as long as MySQL gets to finish. On a clean shutdown it flushes the buffer pool to disk, writes a checkpoint saying “everything up to this point is on disk,” and exits. On the next start it sees the checkpoint, sees a matching log, and starts in a second.

Now consider what XAMPP does instead. Its control panel starts mysqld.exe as an ordinary child process, and when you press Stop — or close the panel, or log off, or Windows restarts for an update — it terminates that process. Not a request to shut down. A kill.

So InnoDB dies with a buffer pool full of changes that were never written and a redo log full of records past the last checkpoint. On the next start it tries crash recovery: replay the log forward from the checkpoint. When that replay hits something inconsistent — a page half-written because the kill landed in the middle of a disk write — it stops. It will not guess. It logs the problem and exits, and XAMPP tells you MySQL shut down unexpectedly.

This is why the error feels random and why it keeps coming back. It is not caused by anything you did wrong in your code or your database. It is caused by how the process was stopped, and under XAMPP that is the same every single time.

Recover your databases

These steps rebuild a working system database from the pristine copy XAMPP ships in xampp\mysql\backup, then put your own data back into it. Work through them in order and do not skip step 5.

  1. Stop XAMPP completely. Close the XAMPP Control Panel. Open Task Manager, go to the Details tab, and end any mysqld.exe still running. If a MySQL process is holding a file lock, every step below will silently half-fail.
  2. Rename the broken folder. Go to your XAMPP folder, open mysql, and rename data to data_old. Do not delete it. It holds the only copy of your databases that exists.
  3. Rebuild a clean one. Copy the whole backup folder (still inside xampp\mysql) and rename the copy to data. You now have an untouched system database MySQL can definitely start against.
  4. Put your databases back. Open data_old. Every folder in there is one database. Copy the ones that are yours into the new data folder. Leave behind mysql, performance_schema, phpmyadmin, and test — the clean versions came from the backup.
  5. Copy ibdata1. Take ibdata1 from data_old and drop it into the new data folder, overwriting the one already there. This is the step people miss. The folders you copied in step 4 are mostly table definitions; the rows themselves live in ibdata1. Skip it and your tables come back empty.
  6. Do not copy ib_logfile0 or ib_logfile1. Those are the corrupted redo logs that caused this. Leaving them out lets MySQL create fresh ones.
  7. Start MySQL. Open the XAMPP Control Panel and start it. Then open phpMyAdmin and check not just that your databases are listed, but that a table you know has rows in it still does.

If that did not work

Fall back to innodb_force_recovery. Open xampp\mysql\bin\my.ini, find the [mysqld] section, and add this line:

innodb_force_recovery=1

Start MySQL. If it comes up, treat it as a rescue window, not a fix: export everything immediately with phpMyAdmin's Export tab or mysqldump, then remove the line and rebuild the data folder from scratch. If it still will not start, raise the number one step at a time. Levels 1 to 3 are generally safe. Levels 4 to 6 can permanently discard data to force a start, so only go there when the alternative is losing everything anyway — and export the moment you get in.

Stopping it permanently

Everything above repairs the damage. None of it touches the cause. If MySQL is still being hard-killed every time you close XAMPP, you have bought yourself some time, not a solution — and most people doing this dance are doing it for the third or fourth time.

The fix is to change how MySQL is stopped. Windows already has the right mechanism: the Service Control Manager. When a program is registered as a Windows service, shutting it down sends it a SERVICE_CONTROL_STOP control code rather than terminating it. MySQL handles that code as a graceful shutdown — flush the buffer pool, write the checkpoint, close the tablespace, exit. And critically, Windows sends it automatically on log-off, restart, and update reboots, so the crash-on-shutdown path disappears even when you are not the one closing anything.

This is the difference visible in the logs. Under a service, a shutdown looks like this:

[Note] Windows service "HelmMySQL": received SERVICE_CONTROL_STOP
[Note] mysqld.exe (initiated by: unknown): Normal shutdown
[Note] InnoDB: Starting shutdown...
[Note] InnoDB: Dumping buffer pool(s) to ib_buffer_pool
[Note] Shutdown completed; log sequence number 44776
[Note] mysqld.exe: Shutdown complete

Nothing to recover on the next start, because nothing was left unfinished.

Two ways to get there

Register XAMPP's MySQL as a service yourself. The XAMPP Control Panel has a checkbox in the left column of each row that installs the service, and it does help. Be aware of the trade-offs: the checkbox requires running the panel as administrator, it does not survive a XAMPP upgrade, recent MariaDB builds removed the mysqld --install flag that some XAMPP versions still rely on, and Apache and MySQL each need doing separately. It is a real fix, but a manual one you have to maintain.

Or use something that does it by default. That is what we built Helm for, after losing the same database one time too many.

The permanent fix

Helm is a free, single-installer replacement for XAMPP on Windows. Apache, PHP, MySQL (MariaDB), and phpMyAdmin all come in one .exe, it installs to C:\Helm, and it registers Apache and MySQL as real Windows services during setup — so MySQL is shut down properly, every time, without you having to think about it.

  • One installer. No separate downloads, no Visual C++ prerequisite hunt.
  • Clean SERVICE_CONTROL_STOP shutdowns by default.
  • Reads mysql_error.log and tells you in plain English what went wrong, instead of listing five possible causes.
  • One-click database backup, so the next scare is a restore rather than a rescue.
  • phpMyAdmin with no login round-trip, and per-project .local domains.
  • Free, MIT licensed, and the source is on GitHub.

Windows 10/11 · 64-bit · Free · MIT licensed

Moving your databases across

If you switch, do it with the databases intact rather than starting over. Recover XAMPP one last time using the steps above, open phpMyAdmin, select each database, and use the Export tab with the default Quick / SQL settings. Install Helm, open its phpMyAdmin at http://localhost/phpmyadmin, create the database, and import the .sql file. Your project files move by copying them into Helm's htdocs folder, which works the same way XAMPP's does.

Common questions

Will I lose my data?

Usually not. MySQL is refusing to start, which is a different thing from your data being gone. The rule that matters: rename the data folder, never delete it. Everything recoverable is in there.

Why does it keep happening to the same project?

It is not the project. It is whichever database had unflushed changes in the buffer pool at the moment of the kill, which is naturally the one you were just working in.

Does reinstalling XAMPP fix it?

It clears the current corruption and loses your databases along the way, and it changes nothing about how MySQL gets stopped. The error returns.

Is this a MySQL bug?

No. InnoDB refusing to start on an inconsistent log is it working correctly — the alternative is silently serving you corrupted rows. The problem is upstream, in how the process is being terminated.

I am on macOS or Linux — same thing?

The InnoDB mechanics are identical, but the trigger is rarer, because MAMP and most Linux setups stop MySQL with a signal it can handle rather than an outright kill. This particular crash loop is largely a Windows and XAMPP problem. Helm is Windows-only for the same reason.