From desktop software like Google Chrome, Apple Photos, and Discord to thousands of mobile apps, SQLite is the embedded storage engine powering daily computing. When an abrupt power loss, operating system freeze, or full disk occurs during a transaction, SQLite can throw the fatal error: database disk image is malformed. Here is how to recover your data.
Understanding SQLite File Structure and WAL Mode
Modern apps operate SQLite in WAL (Write-Ahead Logging) mode. Alongside your primary app.db file, two companion files exist:
app.db-wal: Contains uncommitted write frames waiting to be written to the main database file.
app.db-shm: The shared-memory index file used for coordinating multi-threaded read locks.
Never delete the -wal file when troubleshooting a corrupted database. Deleting the WAL file discards the most recent transactions and guarantees permanent data loss.
Step 1: Emergency Backup Before Any Repair Attempt
Always create read-only copies of all three database files before running any repair utilities:
cp app.db app_backup.db
cp app.db-wal app_backup.db-wal 2>/dev/null || true
cp app.db-shm app_backup.db-shm 2>/dev/null || true
Step 2: Forcing a Passive WAL Checkpoint
Often, a database is not physically damaged; rather, an interrupted transaction left the WAL file unmerged. Force SQLite to merge write frames into the master database:
# Force all outstanding WAL frames into the main DB:
sqlite3 app.db "PRAGMA wal_checkpoint(TRUNCATE);"
# Run quick integrity check:
sqlite3 app.db "PRAGMA quick_check;"
If the command returns ok, your database is repaired. You can safely reopen your application.
Step 3: The Classic Dump & Salvage Pipeline
If quick_check returns B-Tree structural corruption errors, use the SQL dump pipeline to export every readable record into a clean, newly initialized database:
| Method |
CLI Command |
When to Use |
| Standard Dump |
sqlite3 corrupted.db ".dump" | sqlite3 recovered.db |
Best for minor page header corruption where SQL schema remains valid. |
| Native .recover |
sqlite3 corrupted.db ".recover" | sqlite3 recovered.db |
Best for severe page corruption; traverses raw leaf pages directly (SQLite 3.29+). |
# Complete Advanced Recovery Pipeline:
sqlite3 app.db ".recover" > salvage.sql
# Remove corrupt transaction trailers if present:
grep -v "^ROLLBACK;" salvage.sql > clean_salvage.sql
echo "COMMIT;" >> clean_salvage.sql
# Import into fresh database:
sqlite3 app_repaired.db < clean_salvage.sql
Step 4: Verifying Repaired Database Integrity
Before replacing the production file in your application directory, run full structural verification:
sqlite3 app_repaired.db "PRAGMA integrity_check;"
# Must return: ok
sqlite3 app_repaired.db "PRAGMA foreign_key_check;"
# Must return empty output (no orphaned foreign key references)
Frequently Asked Questions
What causes SQLite databases to corrupt in the first place?
The primary causes are: hard system crashes while write buffers are in flight, running SQLite over network drives (NFS / SMB) where file locking is broken, or multiple processes writing to a database compiled without thread safety.
Can third-party GUI tools like DB Browser for SQLite fix malformed databases?
DB Browser for SQLite uses the same underlying C library. For severe B-Tree corruption, the native sqlite3 command-line utility with the .recover extension provides superior data salvage rates.