dbcheck check a file "disk image is malformed" empty backup deleted rows "database is locked" integrity_check says ok

“Database disk image is malformed” after restoring a backup is usually not corruption.

The error names the file, not the cause. In practice it is raised when a page the file claims to have cannot be read, or when the first 100 bytes are not a SQLite header at all — both of which happen to “good” backups, because a backup taken the wrong way is a file with a correct header and nothing in it.

Three different failures, one message

1. The file is a stub (most common)

An app in journal_mode=WAL keeps recent commits in app.db-wal and only periodically moves them into app.db. A copy, a tar, a snapshot, or a “backup” volume that grabs only app.db captures the size the file had at the last checkpoint — often one page. Restoring it produces a 4 KB file that opens without error and contains no tables. The moment you then write to it, or run a repair tool against it, you get the malformed error: the tool was told to expect tables that were never in that file.

2. The file is truncated or was overwritten in place

Header page_count says 13 pages, the file is 5 pages long: the tail is simply missing. This is the one case where the three page counts disagree, and it is the only one integrity_check will also catch.

3. The file is fine and the error comes from the reader

A 32-bit build, a filesystem that returns short reads, or a second process holding the -wal can all produce the same message. Copying the pair to a different machine and checking the bytes first is how you tell this apart from 1 and 2.

What actually helps

How to check before you choose

The check below reads the header, the file length, and any -wal you attach, and says which of the three cases you are in. It takes a few hundred milliseconds and it does not open the file with sqlite3, so it cannot checkpoint or delete your log.

Check one .db file (max 20 MB; add the -wal if you have it)

Read-only byte inspection: page counts, WAL commit, freelist residue. The file is deleted before the answer is written, and nothing about its content is logged.