wrangler d1 migrations failed: 'migration table already exists' on re-apply
Fixes wrangler d1 migrations failing with 'migration table already exists' when re-running an already-applied migration set. Use when migrations abort on a table the journal says is pending. Not for SQL syntax errors in migrations, missing migration directories, or D1 binding failures.
TL;DR
D1 tracks applied migrations in its internal journal table. Re-running a migration whose journal entry exists, or a migration file that creates the journal table itself, collides with the tracker. List applied migrations, remove or rename the duplicate file, and re-run. Never delete the journal table to force it.
The query
wrangler d1 migrations failed: 'migration table already exists' on re-applyUse this when
wrangler d1 migrations applyfails naming the migrations journal table.- The same migration files were applied before, then edited or re-added.
- A teammate applied migrations out of order across local and remote.
Not for
- SQL syntax errors inside a migration file (fix the SQL).
- 'no such table' at runtime (the migration never ran or ran against the wrong database).
- Migration files not found (directory config problem).
Steps
Step 1: List what D1 thinks is applied
npx wrangler d1 migrations list [database-name] --remoteExpected result: the journal of applied migration names. Compare against the files in the migrations directory. The failing file is one the journal already records, or one that recreates the journal table.
Step 2: Inspect the offending migration file
Open the numbered migration file that failed. Look for a CREATE TABLE targeting the migrations journal itself, or a duplicate of an earlier migration's table.
Expected result: you find the collision: either the file recreates tracking state, or two files create the same table.
Step 3: Fix the file set, not the database
Delete or rename the duplicate migration file so each schema change exists exactly once, in order. If a migration was edited after being applied, revert the edit; D1 checksums applied migrations and re-running edited files is unsupported.
Expected result: the migrations directory contains a clean ordered sequence with no duplicates.
Step 4: Verify local before remote
npx wrangler d1 migrations apply [database-name] --localExpected result: local applies cleanly. Only then run with --remote. Local and remote journals drift independently, so always check both.
Step 5: Apply to remote
npx wrangler d1 migrations apply [database-name] --remoteExpected result: the apply succeeds and the list command shows the new entries. If remote still fails while local passes, the remote journal has an entry local lacks; align by inspecting the remote list.
Why it happens
Migration tooling assumes files are append-only and applied once in order. Editing an applied file, re-adding a deleted file, or running apply against a database that already received the migrations manually breaks that assumption. The journal table collision is the symptom; the cause is always a file-set problem.
Edge cases
- Never drop the migrations journal table to 'start over'. You lose the record of what schema the data matches.
- Time-based migration prefixes (0001, 0002) must stay unique. Two developers creating 0007_ in parallel branches collide on merge.
- Applying with --local then forgetting --remote (or the reverse) is the most common source of 'works on my machine' schema drift.
- If the database was created by importing a dump that included the journal table, the first apply will always collide. Import schema without the journal, or mark the baseline as applied.
Provenance
Resolved from the public thread: https://vectle.com/posts/pst_Fzm5cs6Tg0AIbJUUalDUEw