AI agent deleted my production database: how to recover
If an AI agent deleted your production database, stop every write to the database now, make a copy of what remains, and then restore from a backup or from point-in-time recovery. Do not let the agent run more commands. Every new write can overwrite data that you want to get back.
This guide gives the steps in order of urgency. It also shows how to find out what happened and how to remove the access that made it possible.
What does it mean when an agent deletes a production database?
An AI coding agent runs commands for you. It can run SQL, call a migration tool, or run a script. When the agent has a credential for the production database, it can run destructive commands there. A drop table, a delete without a where clause, or a reset command removes the data at once.
The agent does not need bad intent. It tries to complete a task. For example, it sees a schema error. It decides that the fastest fix is to reset the schema and run the migrations again. The command works as it was written, and the data is gone.
Most of these events have the same three causes:
| Cause | What it looks like |
|---|---|
| Production credential on the dev machine | The .env file holds the production connection string |
| Credential with full rights | The agent uses the owner role or an admin key, so every command works |
| No approval step | The agent runs commands without asking a person first |
A prompt such as “never delete data” does not remove these causes. The agent can still send the command, and the database accepts it.
How do I confirm what was deleted?
Check the damage before you change anything. Use read-only commands.
- Stop and write down the time. Note the time at which you first saw the problem. Note the last time that the app worked.
- Connect with a read-only role, or use the dashboard. Do not use the credential that the agent used.
- List the tables. Check that each table still exists.
- Count the rows. Compare the counts with an earlier number from a report, a dashboard, or a backup.
- Check the app. Sign in as a test user and look at the data. Note which parts are empty.
Use these queries in the SQL editor:
select table_name
from information_schema.tables
where table_schema = 'public'
order by table_name;
select relname as table_name, n_live_tup as approx_rows
from pg_stat_user_tables
order by relname;
The second query shows an estimate and not an exact count. Use select count(*) from table_name for an exact value on the tables that matter. If a table is missing, the agent dropped it. If a table exists with zero rows, the agent deleted the rows or truncated the table.
Also check Storage. Files in object storage are separate from the tables. Deleted files can be missing even when the tables are fine.
What do I do first? Fix it now, in order of urgency
Do the steps in this order. Do not skip step 3.
Warning: Do not run vacuum full, do not drop more objects, and do not restore a backup over the current database before you copy the current state. A restore replaces data and cannot be undone.
- Stop the agent. Close the editor session or stop the process. Do not ask the agent to repair the data.
- Stop the writes. Put the app in maintenance mode, or turn off the routes that write. Pause background jobs and webhooks. New rows reduce your recovery options and can conflict with a restore.
- Revoke the agent’s access. Rotate the database password and any API keys that the agent could read. Remove the production credential from the agent’s machine. If the agent used a
service_rolekey, rotate it. See the guide on the service_role key. - Copy what remains. Take a dump of the current database, even if it is damaged. This copy is your safety net if a restore goes wrong. See the command below.
- Find the last good time. Use your notes and the app logs to pick a time just before the deletion.
- Choose the recovery method. Use point-in-time recovery if it is on. Otherwise use the latest daily backup. See the next section.
- Restore, preferably into a separate copy. If your host can restore into a new database, restore there first. Compare the data with the damaged database, then copy back what you need.
- Verify the data. Check the tables, the row counts, and the app with a test user.
- Re-open the app with limited rights. Do not give the old credential back. See the prevention section.
Copy the damaged database first
Use pg_dump with the connection string of the database. The Supabase backup documentation recommends the Supabase CLI db dump command for your own exports. This command is an example for a plain Postgres database:
pg_dump "$DATABASE_URL" --format=custom --file=damaged-$(date +%Y%m%d-%H%M).dump
Keep the file in a safe place that the agent cannot reach. Do not delete it until you finish the full recovery and check.
How do I restore the data?
The method depends on what your host offers. This section uses Supabase as the example, because many AI-built apps use it. Other hosts have similar features. Read the documentation of your host for the exact steps.
According to the Supabase backup documentation, these facts apply:
| Item | What the documentation says |
|---|---|
| Daily backups | Pro keeps 7 days, Team keeps 14 days, Enterprise keeps up to 30 days |
| Free plan | The page lists no daily backups for the free plan. It recommends regular exports with db dump |
| Point-in-time recovery | Available as an add-on for Pro, Team, and Enterprise. It restores to a chosen second |
| Where to restore | Daily backups are in Database > Backups. PITR is in the point-in-time settings |
| Storage files | Backups do not include objects that the Storage API holds. They include only the metadata |
| During a restore | The project is not available. Larger databases take longer |
| Project deletion | When a project is deleted, the data and the backups are removed for good |
Three results follow from this table.
First, a daily backup gives the state at the backup time. All rows that users created after the backup are lost. Tell your users which time window is affected.
Second, a restore does not bring back deleted files in Storage. If the agent deleted files, you need your own copies. Check any other place that holds the files, such as a separate bucket, a local copy, or another service.
Third, point-in-time recovery is far better for this problem. You can choose a time just before the command ran. Turn it on after you recover, if you can afford it.
If you have no backup, check for other copies before you decide that the data is lost:
- Look for a database export that you made earlier. Check your own computer and your cloud drive.
- Look for a staging or development database with a copy of the data.
- Look for CSV exports, email reports, and analytics data that hold part of the data.
- Ask your host support. Some hosts keep internal snapshots that are not visible in the dashboard. Ask quickly, and stop all writes first.
- Check git history for seed files or migration files that contain reference data.
Copy back only the rows you need
A full restore replaces the current state. If the agent only deleted one table, you can restore into a separate copy and move just that table. This keeps the new rows that users wrote after the deletion, if you allowed writes.
pg_dump "$RESTORED_URL" --table=public.orders --data-only --file=orders.sql
psql "$PRODUCTION_URL" --file=orders.sql
Check the foreign keys and the sequences after the import. A table that has an identity column can need its sequence set to the highest ID. Test with a small table first.
How do I find out what the agent did?
You need to know the exact command to be sure that you fixed the cause. Use these sources:
- The agent’s transcript. Open the session history of your tool. Find the commands that ran in the last hour before the failure. Look for
drop,truncate,delete from,reset,--force, and migration commands. - The terminal history. Read the shell history of the machine on which the agent ran.
- The migration files. Check for new files in your
migrationsfolder. An agent may have created a migration that drops a table. - The git log and the diff. Look for changes to schema files, seed scripts, and the
.envfiles. - The database logs. Read the logs that your database host keeps. Many hosts do not record each statement by default, so the logs can be empty for the time of the deletion.
- The connection source. Look at the connection logs for the address that connected. It should match the machine of the agent.
Write down which credential ran the command and which environment it pointed to. This answer tells you which control failed. Most cases show that the credential pointed to production when the developer thought it pointed to development.
How do I stop the agent from doing this again?
Use these controls in this order. Controls 1 to 3 have the largest effect.
- Use separate databases. The agent must work on a development database with fake data. Production credentials must not exist on the machine where the agent runs.
- Use a limited database role. If the agent needs production access, create a role that can only read. Grant write rights only on the tables that a task needs. Do not give
droportruncaterights. - Keep backups and test a restore. Turn on point-in-time recovery for production. Restore a backup into a test database once, so that you know the steps work.
- Require approval for commands. Set your agent to ask before it runs shell commands. Deny the commands that you never want it to run.
- Block destructive commands in the agent settings. Claude Code supports deny rules in
settings.json. The Claude Code permissions documentation shows the format. A rule such asBash(rm *)blocks matching commands. - Do not rely on pattern rules alone. The same documentation says that a deny rule on a command does not match every way to run it. For example, it does not match the program called by path or inside
sh -c. Use a sandbox when the restriction must hold. - Keep production changes in migrations that a person reviews. Run migrations from CI after a review, not from the agent’s terminal.
- Use a protected main branch. Do not let the agent push schema changes to the branch that deploys to production.
- Hide
.envfiles from the agent. AddReaddeny rules for the files that hold secrets, and keep the production values in your host’s secret store.
This example is a deny list for Claude Code. Adapt the commands to your stack:
{
"permissions": {
"deny": [
"Bash(rm *)",
"Bash(psql *)",
"Read(./.env)",
"Read(./.env.*)"
]
}
}
The documentation says that Read and Edit deny rules apply to the built-in file tools and to some file commands in Bash. They do not apply to a script that opens files itself. For this reason, the best control is to keep production secrets off the machine.
For a database role with only read rights, use this SQL as a start. Replace the role name and the password:
create role agent_readonly login password 'use-a-long-random-value';
grant usage on schema public to agent_readonly;
grant select on all tables in schema public to agent_readonly;
alter default privileges in schema public grant select on tables to agent_readonly;
Read database security best practices for more on roles. Read the Claude Code guide and the Cursor guide for the settings of each agent. Use the production security checklist before each release.
A second cause can look the same. If your Supabase tables have no RLS, a stranger can delete rows through the public API with the anon key. Check the logs for requests that did not come from your agent. If you find them, read the Supabase RLS guide.
When should I get help?
Get help in these cases:
- You have no backup and no other copy of the data. Ask the support team of your host at once, and stop all writes.
- The restore fails, or the restored data is not consistent. Stop, keep the dump that you made, and ask a database engineer.
- The deleted data holds personal data of customers. Ask a lawyer whether you must report the loss.
- The logs show a connection from an address that is not your machine. Treat the incident as a security breach, rotate all credentials, and ask for a security review.
- The agent also changed other systems, such as a payment provider or a storage bucket. Check each service and its audit log.
After the recovery, run one more test. Try the agent’s old credential against the database. The connection must fail.
Common questions
Can I recover a database that an AI agent deleted?
Should I ask the agent to fix the damage?
Does a daily backup restore the data from today?
Why did the agent have the rights to delete data?
Can a prompt rule such as 'never delete data' stop this?
Find open write access
A VibeEval scan checks your deployed app for database access that anonymous or low-rights callers can use to change or delete rows.
14-day free trial · No credit card · Cancel anytime