Error: P3009 migrate found failed migrations in the target database
appears when:When a previous prisma migrate deploy failed mid-way, schema drift exists, or the shadow DB user lacks create privileges
Prisma migration failed in production
A partial migration is worse than no migration. Prisma refuses to proceed until you mark the stuck one as applied or rolled back — no amount of retries will fix it alone.
prisma migrate status against the production DATABASE_URL. If it shows a failed migration, either finish the DDL manually and mark it --applied, or revert the partial DDL and mark it --rolled-back. Never re-run migrate deploy on a stuck migration without resolving first — it fails on the same step every time.Quick fix for Prisma migration failed in production
01# Resolve a stuck migration in production (read-only diagnosis first)02npx prisma migrate status03 04# If status shows a migration in the "failed" state:05# 1. Manually finish or revert the DDL in a transaction06psql "$DATABASE_URL" -c "BEGIN; -- inspect the partial state; ROLLBACK;"07 08# 2. Mark the failed migration as resolved, not re-applied09npx prisma migrate resolve --applied "20260410_add_index_users_email"10# or if the DDL was rolled back manually:11npx prisma migrate resolve --rolled-back "20260410_add_index_users_email"12 13# 3. Now safe to run remaining migrations14npx prisma migrate deploy15 16# For drift (prod has schema not in migrations):17# Option A: baseline from prod18npx prisma db pull19# review the diff, commit as an empty migration marked applied20npx prisma migrate resolve --applied "0_init"Deeper fixes when the quick fix fails
01 · Provide an explicit shadow database for managed Postgres
01# .env — pointing at a separate empty DB the Prisma user owns02DATABASE_URL="postgres://user:pw@neon-main.example.com/appdb"03PRISMA_MIGRATE_SHADOW_DATABASE_URL="postgres://user:pw@neon-shadow.example.com/shadow"04 05# On Neon: create a second branch called "shadow" and use its connection string06# On Supabase: create a second project for migrations-only use07# On Railway/Fly: spin up a throwaway Postgres and wire only in dev02 · Baseline an existing production database
01# When prod has schema that is not in any migration (drift)02# 1. Pull the current schema into schema.prisma03npx prisma db pull04 05# 2. Create an initial migration from the current schema06mkdir -p prisma/migrations/0_init07npx prisma migrate diff \08 --from-empty \09 --to-schema-datamodel prisma/schema.prisma \10 --script > prisma/migrations/0_init/migration.sql11 12# 3. Mark it applied without running it13npx prisma migrate resolve --applied 0_init14 15# Now future migrations build on this baseline03 · CI check that catches drift before deploy
01# .github/workflows/migrations.yml02name: Check migrations03on: pull_request04jobs:05 check:06 runs-on: ubuntu-latest07 steps:08 - uses: actions/checkout@v409 - uses: actions/setup-node@v410 with: { node-version: 20 }11 - run: npm ci12 - name: Migration status vs production13 run: npx prisma migrate status14 env:15 DATABASE_URL: ${{ secrets.PROD_READONLY_DATABASE_URL }}16 - name: Migration diff must be empty or additive17 run: |18 npx prisma migrate diff \19 --from-url "${{ secrets.PROD_READONLY_DATABASE_URL }}" \20 --to-schema-datamodel prisma/schema.prisma \21 --exit-codeWhy AI-built apps hit Prisma migration failed in production
Prisma tracks every migration in a meta table called _prisma_migrations. Each row records the migration name, the SQL applied, the checksum, and a status. When you run prisma migrate deploy, Prisma reads the folder of migration files, compares against the rows in _prisma_migrations, and applies anything missing. If one step of a migration fails — say, a CREATE INDEX CONCURRENTLY that hit a lock timeout — Prisma writes a row with finished_at = null and marks it failed. On the next deploy, Prisma sees the stuck row and refuses to continue. The reasoning is correct: re-running a migration that partly succeeded could corrupt the schema.
AI-generated code adds three failure modes on top. First, scaffolds frequently ship with destructive changes in migrations (column drops, type narrowing) that Prisma refuses by default on production databases. You get Data loss warning: this migration will... and the deploy aborts. Second, Lovable and Bolt tend to create a fresh schema.prisma in every session, overwriting your migration history. Third, when working against Neon branches, the branch baseline may diverge from main, so the migration files reference a schema state that does not exist on main.
The dev-only variant is the shadow database error. prisma migrate dev creates a throwaway database to verify the migration applies cleanly to a blank slate. If your Postgres user does not have CREATE DATABASE privileges — as is standard on managed providers like Supabase and Neon — the shadow step fails with permission denied to create database. The fix is to provide a dedicated shadow database via PRISMA_MIGRATE_SHADOW_DATABASE_URL, pointing at a separate empty database the user can drop and recreate.
The last failure is drift: production has tables or columns that are not in any migration. That happens when someone runs raw SQL against prod, or when a previous deployment created schema via prisma db push instead of a tracked migration. migrate deploy detects drift, refuses to proceed, and asks you to baseline. Resolving drift is manual: either db pull the current prod schema into an initial migration and mark it applied, or revert the drifted changes to match the migration history.
Prisma migration failed in production by AI builder
How often each AI builder ships this error and the pattern that produces it.
| Builder | Frequency | Pattern |
|---|---|---|
| Lovable | Every schema rewrite | Regenerates schema.prisma from scratch, overwrites migration history |
| Bolt.new | Common | Uses prisma db push in dev, then tries migrate deploy in prod — drift |
| Cursor | Common | Suggests destructive column renames without a data-preserving intermediate step |
| Base44 | Sometimes | Hard-codes shadow DB URL to main DATABASE_URL — corrupts prod |
| Replit Agent | Rare | Skips migrations entirely, ships with raw SQL on startup |
Related errors we fix
Stop Prisma migration failed in production recurring in AI-built apps
- →Always use prisma migrate dev for schema changes, never prisma db push against a prod-shaped environment.
- →Provide an explicit PRISMA_MIGRATE_SHADOW_DATABASE_URL pointing at a separate empty database.
- →Run prisma migrate status against prod in CI on every PR to catch drift before merge.
- →Write destructive changes as two-step migrations: add new column, backfill, then drop old column in a later migration.
- →Keep the _prisma_migrations table read-only to humans — no manual UPDATE statements outside prisma migrate resolve.
Still stuck with Prisma migration failed in production?
Prisma migration failed in production questions
What is Prisma schema drift and why does it block migrations?+
Why does Prisma need a shadow database?+
How do I resolve a failed migration in production?+
Why does my migration work on Neon branch but fail on main?+
How much does an Afterbuild Labs Prisma audit cost?+
Ship the fix. Keep the fix.
Emergency Triage restores service in 48 hours. Break the Fix Loop rebuilds CI so this error cannot ship again.
Hyder Shah leads Afterbuild Labs, shipping production rescues for apps built in Lovable, Bolt.new, Cursor, Replit, v0, and Base44. our rescue methodology.