Recover a Migration Directory After a Checksum Failure
Reproduce a changed migration file, restore its trusted bytes, and verify that Ptah preserves the deployed history and existing order.
Ptah helps you plan, review, and apply database migrations. Try in your browser
Ptah refuses a deployment because a migration no longer matches ptah.sum.
The immediate question is which bytes changed and where the reviewed copy
is. Rebuilding the checksum before answering that question would accept the
change you are trying to investigate.
This first recovery article reproduces the failure with Ptah 0.13.0 and a disposable SQLite database. The example contains the original files, a trusted copy made before deployment, and the refusal and recovery output.
Recognize the failure
Section titled “Recognize the failure”Our first migration creates an orders table with id and status. We
hashed the migration directory, copied the sealed files into trusted-release/,
applied them, and inserted one queued order.
We then changed status to state in the already-applied up file. The next
verified deployment refused it:
ptah migrations up --db-url sqlite://shop.db --migrations-dir migrations --verify-sumerror: migration sum verification failed:migration directory does not match ptah.sum: changed: 0000000001_orders.up.sqlThe command exited 2. This is a file-integrity failure before a new migration was deployed. It is different from a SQL statement failing halfway through execution, which the MySQL recovery article examines.
Compare with a trusted release
Section titled “Compare with a trusted release”We compared the local file against the copy retained before deployment:
diff -u trusted-release/0000000001_orders.up.sql migrations/0000000001_orders.up.sql--- trusted-release/0000000001_orders.up.sql 2026-10-19 02:15:39.018461428 +0200+++ migrations/0000000001_orders.up.sql 2026-10-19 02:15:39.367136205 +0200@@ -1 +1 @@-CREATE TABLE orders (id integer PRIMARY KEY, status text NOT NULL DEFAULT 'queued');+CREATE TABLE orders (id integer PRIMARY KEY, state text NOT NULL DEFAULT 'queued');The difference explains the refusal: a column name changed in historical SQL. In this exercise we know that the original artifact is correct. In a real incident, establish the reviewed release or immutable artifact first. A checksum establishes consistency with its manifest; it does not by itself authenticate who published the manifest.
Do not edit the migration history table to make the discrepancy disappear. The database already has the original table. If the application now needs a renamed column, that requires a new reviewed migration and compatibility checks, rather than rewriting the description of an old deployment.
Restore the reviewed bytes
Section titled “Restore the reviewed bytes”We restored the changed file from the retained release:
cp trusted-release/0000000001_orders.up.sql migrations/0000000001_orders.up.sqlptah migrations validate --dir migrationsOK: migrations directory matches ptah.sumNo new checksum was needed because the original file again matched the original manifest. Retrying the deployment reported version 1 and zero pending migrations. A separate client still read the existing order:
[(1, 'queued')]The recorded recovery preserved the database and restored the deployment artifact. It did not replay the CREATE statement, rename the live column, or delete migration history.
Choose recovery from the evidence
Section titled “Choose recovery from the evidence”For this incident, the changed file was accidental and the trusted artifact was available. Other checksum failures need their own diagnosis: an extra file, a missing file, or a changed manifest is a different difference to explain. Keep the failed directory for inspection and identify the expected release before replacing anything.
The integrity guide describes the native checks. If bytes match but deployment waits on the database, continue with Find the Transaction Blocking Your Migration.
Get help with an unresolved failure
Section titled “Get help with an unresolved failure”If these checks do not explain your failure, open an issue for a reproducible defect or start a discussion for help choosing a recovery path. Include the Ptah and database versions, the command, a minimal synthetic fixture, and redacted output. Leave credentials and private production data out of the report.
Put Ptah to work
Example files
- build-provenance.json
- check.sql
- measured/baseline.stderr.txt
- measured/baseline.txt
- measured/commands.json
- measured/difference.txt
- measured/hash.txt
- measured/refused.stderr.txt
- measured/refused.txt
- measured/retry.txt
- measured/rows.txt
- measured/verify.txt
- measured/version.txt
- migrations/0000000001_orders.down.sql
- migrations/0000000001_orders.up.sql
- migrations/ptah.sum
- README.md
- seed.sql
- trusted-release/0000000001_orders.down.sql
- trusted-release/0000000001_orders.up.sql
- trusted-release/ptah.sum
- verified.json