The site and the admin panel worked, the script and cron reported no error. Below are the messages as MySQL, mysqldump, WordPress and cron wrote them. In the client's material the database name is replaced with <database>; in the reproduction the shop database is called sklep.
The exact messages
The client's backup file, 751 B, its entire content:
/*M!999999\- enable the sandbox mode */
/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */;
/*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */;
/*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */;
/*!40101 SET NAMES utf8mb4 */;
/*!40103 SET @OLD_TIME_ZONE=@@TIME_ZONE */;
/*!40103 SET TIME_ZONE='+00:00' */;
/*!40014 SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0 */;
/*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */;
/*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */;
/*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */;
CREATE DATABASE /*!32312 IF NOT EXISTS*/ `<database>` /*!40100 DEFAULT CHARACTER SET utf8 */;
USE `<database>`;
The first line is the sandbox mode marker that only the MariaDB client writes, so the dump came from mysqldump of the mariadb-client package on the host (on Debian 12 a link to mariadb-dump), not from the tool in the MySQL container. No comments means --skip-comments, which also removes the closing -- Dump completed on … marker. No footer means the dump was interrupted: an empty database gives a file with a footer (1,173 B in the reproduction).
The client's server log after the restart, about 70 lines in the same second:
[ERROR] mysqld: Table './<database>/wp_options' is marked as crashed and should be repaired
[ERROR] mysqld: Table './<database>/wp_postmeta' is marked as crashed and should be repaired
[Warning] Checking table: './<database>/wp_postmeta'
[ERROR] mysqld: Table './<database>/wp_woocommerce_sessions' is marked as crashed and should be repaired
[Warning] Checking table: './<database>/wp_woocommerce_sessions'
Checking table: only appears when myisam-recover-options is switched on. In the mysql:5.7.44 image it defaults to OFF, so the client's server had it in its configuration.
Reproduction: a full disk. MyISAM does not return an error, it waits for space and holds the table lock, while the home page keeps answering:
[ERROR] Disk is full writing './sklep/wp_woocommerce_sessions.MYD' (Errcode: 15795968 - No space left on device). Waiting for someone to free space...
[ERROR] Retry in 60 secs. Message reprinted in 600 secs
Restarting the database container, the usual reaction. Docker gives the process 10 s, then SIGKILL:
$ docker stop db # 10.5 s, ExitCode 137
[Note] Giving 7 client threads a chance to die gracefully
[Warning] mysqld: Forcing close of thread 31 user: 'sklep'
[ERROR] mysqld: Incorrect key file for table './sklep/wp_woocommerce_sessions.MYI'; try to repair it
[ERROR] Got an error from thread_id=31, .../storage/myisam/mi_write.c:232
WooCommerce's session cleanup a few hours later:
WordPress database error Incorrect key file for table './sklep/wp_woocommerce_sessions.MYI'; try to repair it for query DELETE FROM wp_woocommerce_sessions WHERE session_expiry < 1793638328 ORDER BY session_expiry LIMIT 100 made by ... wc_cleanup_session_data, WC_Session_Handler->cleanup_sessions
From then on, every time the table is opened again it ends with:
ERROR 145 (HY000): Table './sklep/wp_woocommerce_sessions' is marked as crashed and should be repaired
The backup from cron with the damaged table, output of the old script:
Kopia bazy blog
Kopia bazy firma
Kopia bazy sklep
mysqldump: Got error: 145: "Table './sklep/wp_woocommerce_sessions' is marked as crashed and should be repaired" when using LOCK TABLES
Kopia zakonczona: Mon Nov 2 16:53:55 UTC 2026
kod wyjscia skryptu: 0
CRON[107]: (root) CMD ([108] /root/skrypty/backup-mysql.sh)
CRON[107]: (CRON) info (No MTA installed, discarding output)
CRON[107]: (root) END ([108] /root/skrypty/backup-mysql.sh)
The shop file shrank from 285,757,704 B to 743 B, with the current date. With a database name as long as the client's it comes to exactly 751 B.
The damage works like a delayed fuse
After the restart everything worked and the 285 MB backup succeeded. The table was only flagged when WooCommerce's session cleanup (every 12 hours) hit the inconsistent index, and opening it only failed once the table had been reopened. Backups start failing hours or days after the incident, when nobody connects the two any more. Killing the database process alone did not damage the table for good; that took the full disk plus docker stop, and a third run of the same scenario ended with just a warning. Whether a restart breaks a table depends on the moment.
Fixing what you can see leaves the rest
In the second run wp_options failed too, and the shop showed "Error establishing a database connection". WordPress's tool wp-admin/maint/repair.php only checks the 12 core tables plus whatever plugins add through the tables_to_repair filter; WooCommerce adds nothing. The site came back, the damaged session table stayed. On top of that the tool reported "Failed to repair the wp_options table" although the table had been repaired: it only reads the first row of the REPAIR TABLE result, and status OK is in the last one. mysqlcheck --all-databases --check also printed Corrupt and exited with code 0, so as a monitoring check it would have been useless.
One table stops the whole database, and the "fixes" produce worse files
With the default --lock-tables the dump starts with a single LOCK TABLES on every table of the database, so one flagged table stops them all. A 743 B file stands out if anyone looks at the size. --force gives 210 MB missing one table and with a normal ending, --single-transaction gives 210 MB cut off halfway through the table list. Both look like a backup. The new script's partial file was 82 % of the previous backup's size, so it would have passed the common "alarm below 80 %" threshold.
The file date proves nothing, and a name without a date destroys the last good backup
The shell truncates the file at > <database>.sql before mysqldump even connects. The file gets a fresh date every night, and the first failed night replaces the last good backup. The complete version from eight days earlier had to come from another backup of the server.
The script and cron report success
mysqldump returns 2, the script 0, because the last command is an echo. The message goes to stderr, cron tries to mail it, the host has no mail server, so cron discards it and only logs END, without an exit code.
The site and the admin panel show nothing
With the session table damaged, the home page, shop, product page, search, login and every admin page we checked answered 200 without a message. There was one symptom: the guest cart held 0 items after adding a product. The only trace in the admin panel is WooCommerce, Status, Database, which calls the damaged table missing: "Missing base tables: wp_woocommerce_sessions".
Restoring an empty backup "succeeds", and backups can stop working without any failure
Loading the 743 B file ends with exit code 0 and an empty database in every client we tried: mysql 5.7.44, 8.0.46, 8.4.13 and mariadb 10.11. The sandbox marker in the client's file (/*M!) does not bother MySQL clients, but dumps from the older mariadb-dump 10.11.8 start with /*! and load nothing:
ERROR at line 1: Unknown command '\-'.
And after the host was upgraded to Debian 13 (mariadb-client 11.8.6) the same script already fails on the list of databases, because newer MariaDB clients verify the server certificate by default and MySQL 5.7 has a self-signed one. Zero files, exit code 0, "Kopia zakonczona":
ERROR 2026 (HY000): TLS/SSL error: Certificate verification failure: The certificate is NOT trusted.