# Rollbacks and recovery (/docs/orm/next/migrations/rollbacks-and-recovery)

> For the complete Prisma documentation index, see [llms.txt](https://www.prisma.io/docs/llms.txt). A markdown version of any docs page is available by appending `.md` to its URL.

Rolling back is planning one more migration to a state you've already been in. Recovery is fixing the cause and re-running, safely.

Location: ORM > Next > Migrations > Rollbacks and recovery

Two different situations get called "rollback", and Prisma Next treats them differently:

* **The migration applied, but the change was wrong.** You need to move the database *back* to an earlier state. That's a **rollback**, and in Prisma Next it's one more migration.
* **The migration failed partway.** You need to get *unstuck*. That's **recovery**, and it usually means fixing the cause and re-running, because re-running is safe.

## Rollback: a migration like any other [#rollback-a-migration-like-any-other]

There is no `migrate down` command, and no separate "down migration" files. In the [graph model](https://www.prisma.io/docs/orm/next/migrations/the-migration-graph), the state you want to return to is a node you've already visited, so rolling back means planning a new edge that points at it. If you know git, this is `git revert`, not `git reset`: history only ever grows, and the ledger (the applied-history record every database keeps) retains the full round trip.

<ConceptAnimation name="migration-rollback" />

Suppose `20260707T1008_add_display_name` shipped and needs to come back out. Plan the reverse edge; `<dir>^` means "the state before that migration":

```bash
npx prisma-next migration plan \
  --from 20260707T1008_add_display_name \
  --to 20260707T1008_add_display_name^ \
  --name rollback_display_name
```

```text
✔ Planned 1 operation(s)

│
└─ Drop column "displayName" from "user" (destructive)

⚠ This migration contains destructive operations that may cause data loss.

from:   sha256:e6b5c2849eca8d24ff1e8e88ab2a4234db8e74c497c035cb7ce42e814f31cd63
to:     sha256:705b1a62f26f0913caa4bfe3f8b7cb491a1b94bd47fc43471d8711bc480bcbb5

DDL preview

ALTER TABLE "public"."user" DROP COLUMN "displayName";
```

The planner diffs the two contract states and writes the operations that undo the change, flagged **destructive** because they are. This is a real migration: review it, edit it (for example, to archive the column's data into another table before the `DROP`), commit it. Then apply it like any other migration:

```bash
npx prisma-next migrate --to 20260707T1008_add_display_name^
```

A rollback also leaves a cycle in the graph, so the next `migration plan` cannot pick its starting point automatically. It fails with `MIGRATION.NO_TARGET` and lists the reachable states; plan with an explicit `--from <state>` until history moves forward again.

One loose end remains after the database moves back: your contract source still contains the change, so `prisma-next db verify` reports a hash mismatch until you revert the schema and re-run `prisma-next contract emit`. Roll back the contract in the same commit as the rollback migration and the two stay in step.

Afterwards the graph shows the round trip: a forward edge up, a rollback edge back down:

```text
*   e6b5c28  @contract
|^  20260707T1008_add_display_name       705b1a6 -> e6b5c28  3 ops
|v  20260707T1010_rollback_display_name  e6b5c28 -> 705b1a6  1 ops
*   705b1a6
|^  20260707T1005_init                         - -> 705b1a6  2 ops
*   -
```

The database's marker (its record of which graph node it currently matches) is back at `705b1a6`, and the ledger records both the apply and the rollback. Nothing was rewritten or deleted.

Two things to be clear-eyed about:

* **A rollback does not resurrect data.** Dropping the column discards whatever the forward migration and the app wrote into it. If that data matters, edit the rollback migration to save it somewhere first; that's exactly why the rollback is an editable migration and not an automatic mechanism.
* **You don't have to retrace every step.** An edge can jump from the current state directly to any earlier node, skipping intermediate states: one planned migration, one apply, even if you're rolling back three changes.

### One planning caveat after a rollback [#one-planning-caveat-after-a-rollback]

A rollback edge creates a cycle in the graph (`A → B → A`), and with a cycle the planner can no longer infer "the latest state" on its own. The next time you run `migration plan`, pass `--from` explicitly (a migration directory name or hash); the error you'd otherwise get, `MIGRATION.NO_TARGET`, says exactly this.

## Recovery: when a migration fails partway [#recovery-when-a-migration-fails-partway]

A failed `migrate` run stops at the failing operation and reports it precisely:

```text
✖ Operation alterNullability.setNotNull.user.nickname failed during precheck:
  ensure no NULL values in "nickname" (PN-RUN-3000)
  Why: Migration runner failed
  Fix: Fix the issue and re-run `prisma-next migrate --to <contract>` — previously applied migrations are preserved.
```

The playbook:

1. **Read which check failed.** The error names the operation and the specific precheck or postcheck, in plain language. Here, rows with `NULL` still exist, so tightening the constraint would fail.
2. **Nothing to clean up.** On PostgreSQL the whole run was one transaction, so the failure rolled it back completely: the database is exactly where it was before the run, and migrations applied in earlier runs are untouched. There is no "half-applied migration" to untangle by hand.
3. **Fix the cause, then re-run `prisma-next migrate`.** Sometimes the cause is the environment (extension missing, permissions). Sometimes it's the migration itself; here you'd [edit the migration](https://www.prisma.io/docs/orm/next/migrations/editing-a-migration) to add a backfill before the `setNotNull`, recompile with `node migration.ts`, and apply again. Re-running is safe, for the reasons covered in [the failure model](https://www.prisma.io/docs/orm/next/migrations/applying-a-migration#when-something-goes-wrong).

The precheck is also what protects you against the works-in-dev-breaks-in-prod trap. Your dev database had no `NULL`s, production does; the precheck halts production *before* the destructive `ALTER` touches anything, with an error pointing at the exact rows-with-NULLs condition, not a generic constraint violation mid-statement.

### Drift: when the database isn't where migrations left it [#drift-when-the-database-isnt-where-migrations-left-it]

If someone changed the database outside of migrations (a hand-run `ALTER`, a restore from an old backup), the marker or the live schema won't match any state the graph knows, and `migrate` refuses to run rather than pile changes onto drift. Your options, in order of preference:

* **`prisma-next db verify`** checks whether the database still matches your contract, and fails with a precise error when it doesn't.
* **In development**, `prisma-next db update` reconciles the database directly to your contract without walking the graph: quick, but off the record, so treat it as a dev-only reset.
* **For a database with no history at all** (a fresh environment, or adopting Prisma Next on an existing schema), `prisma-next db init` bootstraps it to the current contract and signs the marker.

> [!NOTE]
> What's early
> 
> Reverse planning (`--to <dir>^`), destructive-operation warnings, resumable re-runs, and the ledger all work today, as shown above. Honest gaps: there's no rehearsal mode that executes a migration against a shadow copy first, recovery from drift beyond the `db verify`/`db update`/`db init` trio is manual, and the planning caveat after cycles (explicit `--from`) is a real papercut we expect to smooth out. For deep or unusual situations, `#prisma-next` on [Discord](https://pris.ly/discord) is the fastest route.

## Prompt your coding agent [#prompt-your-coding-agent]

Projects scaffolded with `create-prisma@next` install [Prisma Next skills](https://www.prisma.io/docs/ai/tools/skills#available-skills-for-prisma-next) for your coding agent. Ask your agent to:

* "Plan a rollback for the last migration and show me its destructive operations before I decide."
* "This migrate run failed. Read the error, fix the migration, and re-run it."
* "Check whether staging has drifted from the contract and explain the differences."

## See also [#see-also]

* [Applying a migration](https://www.prisma.io/docs/orm/next/migrations/applying-a-migration): the failure model in the apply flow
* [Editing a migration](https://www.prisma.io/docs/orm/next/migrations/editing-a-migration): fixing a migration that failed for a data reason
* [The migration graph](https://www.prisma.io/docs/orm/next/migrations/the-migration-graph): why backwards is just another edge

## Related pages

- [`Applying a migration`](https://www.prisma.io/docs/orm/next/migrations/applying-a-migration): prisma-next migrate walks the graph from where your database is to where you want it, with a preview, a checkpoint at every step, and safe retries.
- [`Editing a migration`](https://www.prisma.io/docs/orm/next/migrations/editing-a-migration): A migration is TypeScript you own. Fill in backfills, reorder steps, or drop to raw SQL, then recompile it with one command.
- [`Generating a migration`](https://www.prisma.io/docs/orm/next/migrations/generating-a-migration): Turn a contract change into a reviewable migration with prisma-next migration plan.
- [`How migrations work`](https://www.prisma.io/docs/orm/next/migrations/how-migrations-work): Change your contract, plan a migration, review it, apply it. Every step is checked before and after it runs.
- [`The migration graph`](https://www.prisma.io/docs/orm/next/migrations/the-migration-graph): You changed your schema, a teammate changed theirs, both merged. The migration graph is what lets every database catch up without renaming files or rebuilding history.