Skip to content

Backups

An AI agent deleted my Supabase data. What can I recover?

An AI agent deleted your database data. What you can recover was decided before it ran, and the next few minutes decide how much of it survives.

Vlad Tkachenko9 min read

In short

  • If an AI agent deleted your database data, what you can recover is whatever was in the last copy taken before it ran. Nothing made after that copy was ever written down anywhere else.
  • Stop your app writing new rows before you do anything else. A restore removes everything added after the copy, so a signup that arrives now is a row you will delete yourself.
  • The agent cannot put the rows back. It never held them, and running it again against a live database is another chance to lose more.

You asked the agent to tidy up some test rows, or to fix a migration that would not apply. It wrote the SQL itself, ran it against the project your live app uses, and reported that it was done. The app still loads. The table is empty.

When an AI agent has deleted data from your database, what you can recover was decided before it ran. The next few minutes decide how much of that survives.

Here is the part that most answers to this get wrong. They tell you to roll the code back, or to ask the agent to undo it. Neither one touches your data, and while you try them the one thing you do still control is quietly getting worse: what your app is writing to the database right now.

What can I recover after an AI agent deletes my data?

Whatever was in the last copy of your database taken before the delete ran, and nothing that was made after it.

That is the whole answer, and most of it was settled weeks ago. There is no forensic step here, nothing to undelete, no log sitting in Supabase waiting to be replayed. A row removed by a DELETE or a DROP TABLE is gone from the database the moment the statement commits. What comes back is a copy, and a copy either exists or it does not.

So there are two jobs in front of you. Find out which copies exist. And stop the gap between the newest one and now from getting any more expensive than it already is.

Stop your app writing to the database first

Before you go looking for a backup, turn off anything that writes new rows. Reads are fine.

There are two reasons, and the second one catches people.

The first is that a restore is not a merge. Putting a copy back replaces the tables it contains with what those tables held at the time, so everything written after that copy goes with it. A customer who signs up during the hour you spend reading this is a row you will delete yourself, later, in a step you have already decided to take.

The second is that some routes get worse while you wait. Supabase's point-in-time recovery rewinds the whole project to a minute you choose, which means the further the app gets from the mistake, the more genuine work that rewind throws out alongside the damage.

An hour behind a maintenance notice is a cheap thing. Telling somebody their order was created and then deliberately removed is not.

Can I ask the agent to undo it?

No. It never held your rows.

The agent wrote an instruction and your database carried it out. What it has now is a transcript of that conversation, which is a different object from a copy of the table. Ask it to reverse the delete and you get a confident answer, some plausible SQL, and no data.

The risk is worth being specific about, because the request feels harmless. Running the agent again means a second unsupervised session against a live database that has already lost something. If it decides the way to fix a missing table is to create one, you now have a new empty table standing where the old one was, and you have made the loss harder to describe to whoever helps you next.

Take it off the project until you know where you stand. The same goes for your builder's version history: rolling your code back to this morning restores your pages and leaves your data exactly where it is now, for reasons worth understanding once.

Where to look, in order

Work down this list and stop at the first one that exists. It runs in order of how much of your data comes back, which is not the order of how easy they are.

Where to lookWhat it can give backWhat had to be true already
Supabase point-in-time recoveryThe database as it was at a minute you choose, including the one before the deleteA paid plan with the PITR add-on, bought before today
Supabase daily backupThe database as it was at the last automatic copyA paid plan
A managed backup serviceThe database as it was at that service's last checked copyYou connected one before today
A pg_dump you ran yourselfEverything in the file, which is as old as the last time you rememberedThe file exists and finished writing
A soft-delete or audit table in your appOnly the rows your app was already keeping a record ofYou built it that way on purpose
Your builder's version historyNothingIt versions your code, and your data lives in another service.
Asking the agent againNothingIt has never held a copy of your rows.

Every route that returns anything has the same requirement, and it is the one that settles this: it had to exist before the delete ran. None of them can be switched on now and made to reach backwards. Which Supabase plan you are on settles the top two, and checking takes about two minutes.

What you get back, and what falls in the gap

The copy, as it was, and nothing that happened after it.

The rows created between that copy and the delete are not damaged and they are not hiding somewhere awkward. They were never written down anywhere except the database they have been removed from. That is the hard part to accept while you are looking at an empty table, because losing something usually means it is still somewhere.

A restore returns you to the last copy. The rows in the shaded band were never written down anywhere else, so nothing brings those back.

How wide that band is depends entirely on what was taking the copies. A nightly backup can put a whole working day in it. Point-in-time recovery narrows it to a minute, which is most of why it costs extra. A dump you ran when you happened to think of it puts however long ago that was in it.

There is a version of this where the gap barely matters, on an app whose data is mostly your own content and changes slowly. There is a version where it matters a great deal: anything holding orders, messages or signups, where a day of rows is a day of people who will notice.

The agent deleted some of it, not all of it

Then you have a real choice to make, and neither answer is clean.

A restore does not put the missing rows back and leave the rest alone. It replaces the tables in the copy with what those tables held at the time, wholesale. Restoring gets back what the agent removed and reverts every good row written since. Not restoring keeps the good rows and leaves the hole.

Which one is right depends on how much genuine work sits after the copy. If the answer is almost none, restore and move on with your day. If the answer is three days of customer records, the slower route usually wins: export the surviving rows first, restore, then put the exported rows back on top. It is fiddly, and it is the only path that loses nothing.

It is also the argument for copying the database in its current broken state before you touch anything at all. Whichever way you decide, you want to be able to get back to the state you started from.

Copy the broken state before you replace it. It is the difference between a decision you can change your mind about and one you cannot.

How the same hour goes when a copy already exists

Shorter, and mostly a decision rather than a search. You pick a copy, you check what it held, and you press the button.

That is what Reeve Care does: checked copies of your Supabase database on a schedule, encrypted and kept outside your Supabase account, with a one-click restore behind them. Before a restore runs it takes a fresh copy of the current state, so the restore itself can be undone.

The limits belong in the same breath, because knowing them is how you decide whether it fits:

  • It copies your database, and the files your users uploaded once you connect your Storage buckets. Connecting those is optional and takes a second key.
  • It works with Supabase and nothing else today.
  • You restore to a copy that exists, not to any minute you name. Worst case you lose one interval, which is up to a night on the entry plan and shorter on the two above it.
  • Login accounts are never touched. Nobody is signed out, deleted, or brought back.
  • The database key it stores can only read, which was the promise when you connected it. Writing data back needs one that can write, so a restore asks for your database password each time and keeps none of it. The optional key for your files can write, because Supabase issues no read-only key for Storage, and the page where you hand it over says so before you do.

What each plan covers is on the homepage. If you would rather not pay for any of it, the plain version of the same protection is a pg_dump on a schedule you genuinely keep, held somewhere that losing your Supabase account would not also take. The three routes and what each one misses covers that without a sales pitch.

You can see that hour drawn out, copy by copy, on the Supabase backups page.

What to do right now

What to do

  • Stop anything that writes to the database. It is the only step here that gets more expensive the longer you leave it.
  • Establish which copies exist before deciding anything: your Supabase plan first, then any dump you took yourself, then whether your app keeps a record of deleted rows.
  • Copy the database as it is now, broken and all. Whatever you do next, you want a way back to this.
  • After a restore, check the app can still write. A database that reads correctly and refuses an insert is a half-finished restore, and only an insert proves it.
  • Once the immediate thing is over, set up the copy that would have made this a ten-minute problem instead of a morning.

Before you close this tab, open your Supabase project and find out whether anything at all is copying your database, then write down where that copy lives. The 10-minute security checklist covers it alongside the other things worth confirming on a newly launched app.

FAQ

An AI agent deleted rows in my Supabase database. Can I get them back?

Only from a copy taken before it ran, so the first thing to establish is whether one exists. Check what your Supabase plan includes, then check for any dump you took yourself, then check whether your app happens to keep a record of deleted rows. If none of those exist, the rows are gone, and no amount of work on the app will change that. Establish this before you write anything new to the database.

Can I ask the agent to put the data back?

No. The agent never held your rows. It sent an instruction to your database and the database carried it out, so what it has now is a transcript of the conversation rather than a copy of the table. Asking it to reverse the delete gets you a confident answer and no data. It also means a second unsupervised session against a database that has already lost something.

I am on the Supabase free plan. Is there anything to recover?

Not from Supabase. Automatic backups start on the paid plans and point-in-time recovery is an add-on above that, so a free project has no copy of yesterday sitting anywhere. What is left is a dump you took yourself, or a soft-delete or audit table your app was already writing. Check both before you assume the worst, and check them before you keep using the app.

Should I keep the app running while I work out what to do?

Turn off anything that writes. Reads are fine. New rows are a problem twice over: restoring a copy removes everything added after it, so an order that arrives now is data you will delete yourself later, and a point-in-time rewind has more real work to throw away the longer you leave it. A maintenance notice for an hour costs less than the second loss does.

The agent deleted some rows but not all of them. Do I still restore everything?

That is the awkward case and there is no clean answer. A restore replaces the tables in the copy wholesale, so it brings back what you lost and also reverts every good row written since. Which way to go depends on how much real work sits after the copy. If it is a lot, exporting the surviving rows first and putting them back on top of the restore is slower and loses nothing.

Written by

Vlad Tkachenko

Founder, Reeve

I spend my time looking at apps built with Lovable, Bolt, v0, Cursor and Replit, and at the short list of mistakes that keep turning up in them.

More about the author

Read next

Not sure where your own app stands?

Run a free scan and get a plain-language grade from A to F in about 20 seconds. No account, no card.

Scan your app free

Automated external check, not a full audit. Absence of findings is not a guarantee of safety.