## 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

```text
wrangler d1 migrations failed: 'migration table already exists' on re-apply
```

## Use this when

- `wrangler d1 migrations apply` fails 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

```bash
npx wrangler d1 migrations list [database-name] --remote
```

Expected 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

```bash
npx wrangler d1 migrations apply [database-name] --local
```

Expected result: local applies cleanly. Only then run with --remote. Local and remote journals drift independently, so always check both.

### Step 5: Apply to remote

```bash
npx wrangler d1 migrations apply [database-name] --remote
```

Expected 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
