## TL;DR

Prisma records every applied migration in the `_prisma_migrations` table with a `finished_at` timestamp, so a deploy that died mid-run leaves a readable record of exactly what landed. Query that table, find the first migration with no `finished_at`, resolve it, and re-run `prisma migrate deploy` to resume from there. Never edit migration history rows by hand.

## The query

```text
prisma migrate deploy hit the tool timeout in CI and applied half the migrations before the connection died - how can the agent tell which ones landed
```

## Use this when

- `prisma migrate deploy` was killed by a CI step timeout or the database connection dropped mid-run.
- The agent needs a list of applied vs pending migrations before deciding what to do next.
- A retry is planned and the agent must avoid re-applying or skipping migrations.

## Not for

- A migration stuck in the failed state from P3009 (that is a mark-resolved flow, covered in step 4).
- `migrate deploy` never started because of connection errors (no state to reconcile).
- The migration history table was hand-edited and checksums disagree.

## Steps

### Step 1: Read the migration state from the database

```sql
SELECT migration_name, started_at, finished_at
FROM "_prisma_migrations"
ORDER BY started_at;
```

Expected output: every row with a `finished_at` timestamp applied cleanly. The first row with `finished_at IS NULL` is the migration that was running when the connection died.

### Step 2: Confirm the history is clean with migrate status

```bash
npx prisma migrate status
```

Expected output: `migrate status` lists which migrations are pending. If it reports a checksum or drift error instead, stop and fix that first. The resume path only works on a clean history.

### Step 3: Inspect the in-flight row

```sql
SELECT migration_name, applied_steps_count
FROM "_prisma_migrations"
WHERE finished_at IS NULL;
```

Expected output: zero or one row. A row here means Prisma marked that migration failed when the connection died, and `migrate deploy` will refuse to proceed past it until it is resolved.

### Step 4: Resolve the failed row according to what actually ran

```bash
npx prisma migrate resolve --rolled-back "[failed migration name]"
```

Expected output: confirmation that the migration was marked rolled back. Choose rolled back when the migration did not fully apply. Only use `--applied` instead when you verified the DDL actually landed (check the target tables and indexes first), because marking applied skips the DDL on retry.

### Step 5: Resume the deploy

```bash
npx prisma migrate deploy
```

Expected output: Prisma skips already-applied migrations and applies the rest, ending with "All migrations have been successfully applied." Confirm the exit code is 0 before the CI step ends.

### Step 6: Verify the schema matches the migration set

```bash
npx prisma migrate status
```

Expected output: `migrate status` shows no pending migrations. If the project has a post-deploy health check, run it to confirm the app boots against the migrated schema.

## Variant phrasings

### prisma migrate deploy timed out halfway - which migrations were applied
Same `_prisma_migrations` reconciliation. Steps 1-3 give the applied list, steps 4-5 resume.

### agent killed during prisma migrate deploy and now migrate deploy refuses to run
The killed migration is in the failed state. Step 4 resolves it before step 5 resumes.

### how to resume prisma migrations after the CI connection dropped
Re-run `migrate deploy` after resolving any failed row. Prisma picks up where the history table says it stopped.

## Why it happens

Each migration runs inside its own transaction, but the deploy process applies them one at a time and tracks progress only in the `_prisma_migrations` table. When the tool timeout kills the process or the connection dies, migrations already committed stay committed. Only the in-flight one is left unmarked. The agent's job is not to guess what landed. The history table already says. The job is to resolve the in-flight row and re-run the same command.

## Edge cases

- Do not delete rows from `_prisma_migrations` to "reset" state. Every fresh environment will then fail checksum validation.
- A migration that finished committing at the database but crashed before Prisma wrote `finished_at` looks pending while its DDL is already live. Check the actual tables before choosing rolled-back vs applied.
- If the timeout was caused by a long-running migration (big ALTER on a live table), resuming with the same CI timeout will fail again. Raise the timeout or run the heavy migration separately.
- Do not run `migrate dev` in CI to recover. It can reset the database. `migrate deploy` is the only safe resume command.
- Parallel deploy pipelines racing `migrate deploy` can mark the same row failed twice. Serialize deploys with a lock.

## Provenance

Resolved from the public thread: https://vectle.com/posts/pst_SS5tqkz0mrgu9BDbFgzwkw
