Fix it now
- Drop the file
- Repair runs locally
- Download the result
WhatsApp on Android stores every conversation in a single SQLite database named msgstore.db — message text, timestamps, chat and contact references all live inside it. When an app crash, an interrupted copy off the phone, or a half-finished sync damages it, WhatsApp and every other SQLite tool refuse the whole file with "database disk image is malformed". Drop the decrypted msgstore.db above and the tool walks its pages locally, rebuilds the schema, and re-inserts every row it can still decode into a fresh, openable database — the chat DB, which holds your entire message history, is never uploaded.
What's inside msgstore.db — pages, tables and the -wal sidecar
A WhatsApp database is an ordinary SQLite file, so it's a flat array of fixed-size pages (SQLite's default is often 4 KB). The very first 16 bytes are the magic string SQLite format 3\0; the rest of page 1 holds the file header and the sqlite_schema that lists every table. Each table is a b-tree: interior pages carry pointers down to leaf pages, and the leaf pages hold your actual rows.
The tables that matter for chat history are predictable. Modern WhatsApp builds keep messages in a message table (older builds used messages), the conversation list in chat, and the phone-number/group identifiers in jid. Message text lives in a text column (text_data on the modern schema, data on the legacy one), and every row carries a timestamp in Unix epoch milliseconds. Supporting tables like message_media, message_thumbnail, message_quoted and call_log round it out. Crucially, the photos, videos and voice notes themselves are not in the database — those are separate files in the WhatsApp Media folder; the DB only stores their file paths plus small thumbnails.
WhatsApp runs the database in WAL (write-ahead logging) mode, so on the phone you'll see two sibling files next to it: msgstore.db-wal and msgstore.db-shm. Recent, committed messages can live only in the -wal file until they're checkpointed back into the main .db. If you have that sidecar, keep it — its committed frames are the newest rows.
Why it reports "malformed" — and the .crypt trap
SQLite raises SQLITE_CORRUPT (result code 11) — surfaced as "database disk image is malformed" — the moment it follows a pointer and lands on something that isn't a valid b-tree page: a zeroed page, a bad cell offset, a page count that disagrees with the file size. It stops at the first inconsistency, which is why one damaged interior page takes the whole file down while the untouched leaf pages behind it still hold your messages. A related error, "file is not a database," means the 16-byte header magic itself was mangled — the pages behind it are often fine.
The damage usually comes from an interrupted write. Pulling msgstore.db off a phone over USB or through a file manager while WhatsApp still has it open can copy a torn, half-checkpointed file; a byte flip on a failing SD card or in a sync, or a device that lost power mid-write, does the same. A truncated copy simply loses whatever pages were at the tail.
One trap catches people first: the file in WhatsApp's backup folder is not a plain database. Backups are named msgstore.db.crypt14 or msgstore.db.crypt15 and are AES-GCM encrypted (the key sits at /data/data/com.whatsapp/files/key, or, for crypt15, behind your end-to-end backup password). A .crypt14/.crypt15 file reads as high-entropy noise — it has no SQLite header at all, so page recovery can't touch it until it's decrypted back to a plain msgstore.db. This tool works on the decrypted database, not the encrypted backup.
What recovery pulls back — and why you never upload a chat DB
Instead of trusting the broken pointers, the tool reads the file page by page: it reconstructs the schema, walks each table's b-tree, and for anything the pointer chain can no longer reach it falls back to a raw page sweep that decodes the self-describing records straight out of the leaf pages. It rebuilds the b-trees from scratch around the rows it finds and writes them into a brand-new database you download — the same strategy as SQLite's own .recover command. Rows that can't be attributed to a known table are placed in a lost_and_found table rather than discarded, and if you supply the msgstore.db-wal sidecar its committed frames are overlaid first so the result reflects your last committed messages, not just the last checkpoint.
The honest limits: bytes that were physically overwritten or truncated off the end are gone, and no tool can invent them. Values come back as their on-disk storage class, so a column's affinity may shift. Because the sweep also reads freelist and unvacuumed pages, some previously-deleted messages can resurface. Recovered data is always suspect — SQLite's own docs say so — so verify it against a known-good source before relying on it. And the media blobs still live in the separate Media folder; the database gives you the text and the references, not the pictures themselves.
Why do this in the browser? A chat database is the single worst file to hand to a server you don't control — it can hold every message, phone number and timestamp you've ever exchanged. Upload-based "repair" tools copy that file onto infrastructure you can't inspect, under retention terms you didn't write. Here the file is read from your disk and rebuilt in your tab; you can open the Network panel and confirm 0 bytes of the database leave your machine.
What this can and can't fix
Can fix
- "Database disk image is malformed" (SQLITE_CORRUPT 11) from a damaged page or a broken b-tree pointer
- "File is not a database" when only the 16-byte header magic was mangled and the pages behind it survive
- A msgstore.db torn or half-checkpointed by copying it off the phone while WhatsApp was still open
- Truncated databases — every message row on a surviving page is pulled out, plus uncheckpointed rows from a supplied -wal sidecar
- Rows on pages the pointer chain can no longer reach, recovered by a raw page scan into lost_and_found
Can't fix
- Encrypted .crypt14 / .crypt15 backups without the key — they're ciphertext with no SQLite header and must be decrypted first
- Messages physically overwritten or truncated off the end of the file — those bytes no longer exist
- The photos, videos and voice notes themselves (they're separate files in the Media folder, not in the DB)
- A guarantee that every recovered message is complete or correct — verify recovered data against a known-good source
- 0-byte files, or a database whose header and pages are all noise (that's a storage-recovery problem first)
If a repair fails, we tell you why (missing data versus broken structure), and you are never charged for a failed repair.