The short answer
If a coding agent can reach your production DATABASE_URL with the role that owns the tables, prisma migrate reset will empty it. Prisma 6.15 and later refuse migrate reset and db push --force-reset from agents they detect, until you consent.34 That check is real and useful, but it can be stepped around. The fix that holds is a database role that doesn’t own the schema, so the reset fails with a permission error.
Below: what happened in the public cases, a run of Prisma’s check inside Claude Code, three ways past it, and the role that stopped all three.
Deedglass
A database change, checked and put back
-
1The agent’s change
Change order 42
UPDATE orders … WHERE id = 42
The agent: “1 row updated” -
2What the database really did
301 rows
A hidden trigger changed 300 more
Doesn’t match -
3Put back
301 rows restored
Signed off by a person
Matches
What happened in the public cases
Two reports on the Claude Code issue tracker describe the pattern. In one, filed on 19 March 2026, the agent ran prisma db push --force-reset against a production database.1 In the other, filed on 11 March 2026, a destructive prisma db push ran against production.2 Neither involved an exotic attack. The .env file pointed at production, the agent was asked to change the schema, and a command that resets the database was the shortest path to “in sync”.
A third report shows how a reset can arrive by a side door. In claude-code#80868, a compound command passed DATABASE_URL from .env as --shadow-database-url to prisma migrate diff, no prompt appeared despite ask rules for Prisma commands, and the production database was wiped, then restored from a nightly backup.8 Prisma resets a configured shadow database as part of its work.5
What these have in common: the credential the agent held could drop every table, and nothing between the agent and the database said no.
What Prisma’s own check does
Prisma added a guard in ORM 6.15.0, released on 27 August 2025. Its announcement names Claude Code, Gemini CLI, Qwen Code, Cursor, Aider and Replit as tools that get an explicit confirmation step.3 The current docs say the agent should explain the action, warn that it destroys all data, and only after you consent set PRISMA_USER_CONSENT_FOR_DANGEROUS_AI_ACTION and rerun.5
We ran it. Prisma 6.19.3, a throwaway Postgres 17.11 on a laptop, one table with 200 rows, and the command sent from inside a Claude Code session:
Code What the screen or file shows, copied as is.
$ npx prisma migrate reset --force
Datasource "db": PostgreSQL database "app", schema "public" at "127.0.0.1:56101"
Error: Prisma Migrate detected that it was invoked by Claude Code.
You are attempting a highly dangerous action that can lead to devastating
consequences if it is incorrectly executed against a production database.
…
$ psql -tAc 'select count(*) from "Lead"'
200
The rest of the message tells the agent to stop, explain the command and its motive, say the data will be gone, and ask you. prisma db push --force-reset got the same refusal, and the 200 rows stayed.4
How does Prisma know? It reads environment variables. In the 6.19.x source, Claude Code is detected by CLAUDECODE, Gemini CLI and Qwen Code by GEMINI_CLI, Cursor by CURSOR_AGENT, Aider by OR_APP_NAME, Replit by REPLIT_CLI and Codex CLI by CODEX_SANDBOX. The check runs for migrate reset and for db push --force-reset.4 If the consent variable is set to anything at all, the command goes ahead.4
That design is sensible for what it is: a speed bump that turns a silent reset into a question the agent has to bring back to you. It is not a permission system, and Prisma doesn’t claim it is.
Where the check stops
We tried three ways past it on the same database. Each time we put the 200 rows back first.
| Attempt | What we ran | Rows after |
|---|---|---|
Consent variable set to x |
PRISMA_USER_CONSENT_FOR_DANGEROUS_AI_ACTION=x npx prisma migrate reset --force |
0, in 1.4 s |
| Agent variable not passed | env -u CLAUDECODE npx prisma migrate reset --force |
0 |
| A command the check doesn’t cover | npx prisma db push --accept-data-loss, after removing email from the schema |
200 rows, but the email column and its 200 values gone, in 28 ms, no question asked |
The first is the one to worry about. The variable is meant to carry your words, but nothing checks that it does; an agent that reads the error can set it itself. The second happens whenever the agent’s environment doesn’t reach the process: a remote shell, a script launched by another tool, a CI job. The third is by design: --accept-data-loss tells Prisma to ignore its data-loss warnings, and --force-reset resets the whole database before pushing.6
And Prisma is one tool. drizzle-kit push --force accepts every data-loss statement without asking, and alembic downgrade base reverts every revision back to an empty schema.11 Prisma’s own team makes the same point: their newer stack has no reset command at all, and they concede that no tool can stop a process holding the keys from opening a raw connection.7
The fix that held: a role that can’t drop
In Postgres, only a table’s owner, its schema’s owner or a superuser can drop it.12 So we gave the app a role that can read and write rows but owns nothing:
Code A database command in SQL. The text around it says what it does.
CREATE ROLE app_rw LOGIN;
GRANT USAGE ON SCHEMA public TO app_rw;
GRANT SELECT, INSERT, UPDATE, DELETE ON ALL TABLES IN SCHEMA public TO app_rw;
GRANT USAGE ON ALL SEQUENCES IN SCHEMA public TO app_rw;
Then the worst case: the consent variable set, the URL pointing at app_rw.
Code What the screen or file shows, copied as is.
$ PRISMA_USER_CONSENT_FOR_DANGEROUS_AI_ACTION=x \
DATABASE_URL="postgresql://app_rw@127.0.0.1:56101/app" \
npx prisma migrate reset --force
Error: P3016
The fallback method for database resets failed, meaning Migrate could not
clean up the database entirely. Original error:
ERROR: must be owner of table _prisma_migrations
$ psql -tAc 'select count(*) from "Lead"'
200
db push --force-reset failed the same way, and db push --accept-data-loss stopped at must be owner of index. All 200 rows and the email column stayed.4 No hook, no prompt, no environment variable: the database refused.
The cost is that app_rw can’t run migrations either. That’s the point. Keep a separate owner role for migrations, used by your deploy pipeline or by a person, and never put its URL where the agent can read it. Our guide to giving Claude Code or Cursor write access to Postgres has the full role setup, column grants and row-level security, with nothing to install.
A role doesn’t help with DELETE FROM on tables it can write, of course. For that, see how big a mistake an agent can make and the post on safe Postgres access for AI agents.
Keep the production URL away from the agent
Every public case starts with a production URL in a file the agent reads. Three habits close that door:
- Point
.envat a local or branch database. Production credentials live in your deploy platform’s secrets, not in the repository’s working tree. - Deny reads of
.envin the agent. In Claude Code aReaddeny rule such asRead(.env)matches at any depth and covers Claude’s file tools and recognised file commands likecat, though not a program that opens the file itself.9 - Don’t trust ask rules alone. Claude Code checks deny, then ask, then allow, and ask rules apply when any part of a compound command matches.9 Even so, #80868 shows a compound command that wiped a database with ask rules in place.8
A hook as the second check
A hook reads each shell command before it runs, whatever environment variables the process gets. This is the Deedglass shell guard, v0.9.0, on the commands from the cases above:
Code What the screen or file shows, copied as is.
$ deedglass guard shell --command "npx prisma db push --force-reset"
Deedglass asks first: prisma db push --force-reset resets the database before it
pushes the schema, which deletes every row.
$ deedglass guard shell --command "cd api && PRISMA_USER_CONSENT_FOR_DANGEROUS_AI_ACTION=yes npx prisma migrate reset --force"
Deedglass asks first: prisma migrate reset drops the database, or every table in
it, and applies your migrations again, so its data is gone.
$ deedglass guard shell --command "npm run db:fresh"
Deedglass asks first: prisma migrate reset (in npm run db:fresh) drops the database,
or every table in it, and applies your migrations again, so its data is gone.
The consent variable doesn’t switch it off, and the last line comes from reading the package.json script that npm run would start.10 As a Claude Code PreToolUse hook it answers ask, never allow, and needs no server or account.10
Its limits are the reason it’s the second check, not the first. It reads the command as written and doesn’t run it, and if it can’t run at all, the command goes ahead unchecked.10 It only knows the commands in its catalog, so a reset hidden in a compiled binary or a script it can’t read gets through. A role that owns nothing doesn’t have those gaps.
A checklist
- The URL in
.envpoints at a local or branch database, never production. - The app and the agent connect as a role that owns no tables.
- Migrations run as a separate owner role, from CI or by a person.
- Prisma is 6.15 or later, so detected agents get the consent step.3
- The agent can’t read
.env(aReaddeny rule, or no such file in its tree). - A before-a-command hook asks on reset, force and drop commands.
- Point-in-time recovery is on, and you have restored from it once.
- You know the first hour: what to do when an agent deletes the database.
Try it on your own project: npx deedglass scan reads your Claude Code settings and package.json and lists the database commands your agent could run today. Nothing leaves your machine. The hooks post shows the same guard in Codex, Gemini CLI, Copilot and Cursor. The grade is explained on /score. To watch a database change more than the statement named, try the playground, a real Postgres in a browser tab.
Questions people ask
Does Prisma stop Claude Code from running migrate reset?
Yes, from 6.15.0 on, when it detects the agent: Claude Code is detected by its CLAUDECODE environment variable, and the command stops until the consent variable is set.4 Any value works, so treat it as a prompt for the agent, not a lock.
Is prisma db push –force-reset covered too?
In the code, yes: the same check runs for db push --force-reset.4 The docs describe it for migrate reset, and db push --accept-data-loss isn’t covered.5
Can I use prisma migrate reset in production?
Not against data you want to keep. It drops or soft-resets the database and re-applies every migration, so whatever the tables held is gone.5 Run it on local or branch databases only.
What is the safest way to let an agent run migrations?
Let it write the migration file, and let a person or your deploy pipeline apply it with the owner role. The agent’s own role owns nothing, so a reset fails.
Written with AI assistance and checked against our claims register.