Fix Database Errors in a Vibe-Coded App (Step by Step)
Database errors in a vibe-coded app usually have five causes. Find the real error, fix the schema, query, or policy behind it, and test before you ship.
By Muhammad Saif and Aashan Khan · Techtribe co-founders
A form that will not save. A list that loads for you and stays empty for your users. A dashboard that worked in the demo and now returns a red toast on every click. In a vibe-coded app, these are almost always database errors, and they feel worse than they are: the screen in front of you looks broken everywhere at once, while the actual cause is usually one wrong assumption in one place.
This guide walks the repair the way we do it on a scope call: find the exact error, match it to one of five causes, fix it in the right layer, and test so the fix does not become the next bug. It targets apps built with Lovable, Bolt, v0, Replit, and similar tools, most of which sit on Supabase, but the method applies to any SQL database behind an AI-generated frontend.
Why vibe-coded apps hit database errors so often
The pattern is structural, not bad luck. An AI builder generates a frontend and a schema in the same session. The schema exists to make the screens work right now. Nobody asked what happens when the second feature arrives, when a column gets renamed, or when the demo data is replaced by a real account with 24,000 rows.
Four properties of generated code make database errors more likely and harder to read:
- The query and the schema drift apart. A screen's query references columns from an earlier version of the table. When a later prompt "cleans up" the schema, the query still asks for what used to exist. Nothing warns you until the screen loads.
- Errors get swallowed. Generated frontends commonly catch a failed request and render an empty list instead of showing the failure. An empty screen is a database error with the message thrown away.
- Migrations live in a chat, not in the repo. The table was created in the SQL editor or the dashboard during the demo. There is no migration file, so the staging project and the production project quietly differ.
- Permissions look like bugs. With row level security in play, a query that returns zero rows is often a policy problem, not a query problem — and the next prompt, "make the data show up," is how tables end up open.
None of this means the app is unrecoverable. It means the repair starts with evidence instead of with a new prompt.
Step 1: Get the exact error, not the symptom
The single highest-value minute in this whole process: find the real error text. Symptoms lie; error messages do not.
In the browser. Open DevTools, reproduce the failing action, and read two things. The Network tab shows the failing request — its status code, and the response body, which for Supabase's Data API is usually a JSON object with a message, code, and details. The Console shows whether the frontend caught the error and replaced it with an empty state. If the list silently shows nothing while the request returned 401 or 42501, you are looking at a swallowed error.
In Supabase. The Dashboard's Logs section shows Postgres and API logs for the window when the failure happened. Look for the same timestamp you reproduced. Two log lines matter: the API request line (status, path, the project ref it hit) and any Postgres error line (ERROR: ... with a SQLSTATE code).
Write down three things before touching anything:
- The exact message and error code (for example
relation "public.orders" does not exist, or SQLSTATE42P01). - Which action triggered it, with which account, at what time.
- Whether the same action works for a different account or in a different environment.
That third question splits the field in half. If it works on your machine and fails for a user, suspect environment or permissions. If it fails everywhere, suspect schema or query.
Step 2: Match the error to one of five causes
Once you have the message, the cause usually falls into one of these five. Each has a signature, a fix, and a trap.
1. Wrong project, wrong environment
Signature. Errors like relation "public.orders" does not exist on a table you can see in the dashboard — or results that match a different dataset entirely.
Cause. The client is pointed at the wrong Supabase project, or a different environment's URL and keys. In generated apps this happens through a duplicated .env, a second Supabase project created during the demo, or a preview deployment carrying production keys. Our note on owning your Lovable app's code covers how easily two environments diverge.
Fix. Compare the project URL and publishable key in the running app against the project you are inspecting. One line in .env decides which database every query hits.
The trap. "Fixing" the schema of the project you are looking at while the app queries a different one. You will change the right thing in the wrong place and conclude the app is cursed.
2. Missing migration: the table or column never existed here
Signature. relation "public.x" does not exist, or column "x" does not exist.
Cause. The table was created in the demo project's SQL editor, and the current project never got it. Or a later prompt renamed a column and the migration was applied to one environment only.
Fix. Put the DDL in a migration file in the repo, version it, and apply it to the target project. If the table exists elsewhere, diff the two schemas (information_schema.columns in both projects) and reconcile rather than retyping DDL from memory.
The trap. Creating the table by hand in the dashboard again. That works once and guarantees the next environment drifts further. Migrations in the repo are the whole fix; the dashboard is for reading, not for schema changes you intend to keep.
3. Query written for a shape the data no longer has
Signature. The request succeeds (200) but returns null fields, the frontend throws a type error, or a form submission fails validation against the payload.
Cause. The query selects a column that was renamed or dropped, or inserts a row missing a column that a later prompt made required (a not null constraint with no default). This is the classic drift: the screen and the schema came from different versions of the conversation.
Fix. Align the two — either update the query to the current schema or add the column back with a sensible default. Make the change in one place and redeploy both sides together.
The trap. Patching the type error in the frontend by making the field optional. The error moves from the database to the product: the form now accepts rows that are missing data nobody notices until an export or a report breaks.
4. Row level security filtering the rows to zero
Signature. The query returns an empty array with no error, for a user who demonstrably owns rows — or one user sees data and another never does.
Cause. RLS is on, and the policy does not match how the query identifies the owner: a policy on user_id while the app writes owner_id, a policy scoped to authenticated while the request carries only the anon key, or an expired session the UI still treats as logged in.
Fix. Read the policy against the actual query, then test with two accounts. The full failure catalog — policies that trust editable metadata, missing with check, views that bypass table policies — is in our Supabase RLS mistakes note, which is worth reading before you edit any policy.
The trap. The "fix" that ends the fastest: using (true). The error disappears because the lock is gone. Empty rows mean the gate is closed; deleting the gate is not a repair, it is an incident. If your instinct is to make data show up before you understand why it hid, stop and read the policy first.
5. Connection and quota exhaustion
Signature. too many connections, connection timeout, remaining connection slots are reserved, or errors that appear only under load and vanish when traffic drops.
Cause. The app opens a new direct connection per request — common in serverless deployments where generated code instantiates a client inside the request handler. The pool limit arrives sooner than the traffic growth that triggered it.
Fix. Move to a pooler with a bounded connection count, reuse the client across requests, and check which connection mode fits how the app uses sessions. Supabase documents pooler behavior and per-plan limits in connection pooling and limits.
The trap. Raising the database size before fixing the connection-per-request pattern. It postpones the error and raises the bill; the pattern will hit the next tier's limit the same way.
Step 3: Fix it in the right layer, smallest change first
With the cause identified, resist the urge to fix everything you notice. One error, one change, one test.
Write the failing case down as a test. Even manually: the account, the action, the expected result. You need it in two minutes to confirm the fix, and again tomorrow to confirm nothing regressed.
Make the smallest change that addresses the cause. A missing migration gets a migration. A wrong query gets a query edit. A policy mismatch gets a policy edit. Do not combine a schema change with a refactor of the data layer; if something breaks, you will not know which half did it.
Never ship the visible-but-widening fix. Dropping a constraint, disabling RLS, catching the error and rendering an empty list, or hard-coding the demo account's id — each makes today's error disappear and installs a harder problem. Generated code reaches for these because the prompt rewarded a working screen. You are the reviewer the prompt did not have.
Check who else uses what you changed. A renamed column has more than one caller. A policy change affects every query on that table. Grep for the column name, and re-run the main flows for both test accounts, not only the screen that reported the failure.
If the fix touches auth, payments, migrations, or anything you cannot explain after reading it, that is the signal to get a second pair of eyes rather than to keep prompting. The wider triage order for a generated product lives in fix a vibe-coded app.
Step 4: Test like a user, then ship with a rollback
Database fixes fail in testing in a predictable way: they are verified against the environment where they were made, by the person who made them, with the account that already worked.
Test all four combinations:
- Two accounts, real flows. User A saves, reads back, edits, deletes. User B does the same and must never see A's rows.
- The failing action specifically. The exact form, the exact export, the exact dashboard that showed the error — not a smoke test of the homepage.
- The wrong direction. Try to read and update another account's row by id through the app's own client. A fix that closes the error and opens cross-account access is a net loss.
- The anon key with no session. Sensitive tables must return nothing. This takes thirty seconds and catches the
using (true)class of accident.
Then ship it the way you would ship any change that touches user data: as a migration in the repo, applied in order, with the previous state documented so you can reverse it. Keep the error message from Step 1 in the pull request description. The next person to read it should not have to reconstruct why the change exists.
Watch the logs for the first day after deploy. The first real user with real data is a test case your demo account never was — our note on apps that break in production is about exactly that gap.
When the database error is a symptom of something deeper
Most database errors are shallow: one migration, one query, one policy. A minority point at the schema itself, and it is worth knowing the difference before you invest a weekend.
Bring in a developer — or scope a repair — when any of these hold:
- The schema cannot represent the product. Every new feature requires a column that breaks an old one, or two screens need contradictory versions of the same row.
- Nobody can say what the data means. If you cannot explain which table owns a user's orders without opening three files, the next fix will create a fourth.
- The error list keeps growing. You fixed four this month and two appeared. That is drift, not a bug: the app is being changed faster than it is being understood.
- Real money or real users are on it. At that point the cost of a wrong fix — a lost payment, a leaked table — exceeds the cost of a scoped repair.
In our experience the repair is still usually small: the schema gets one migration and a comment, the queries get pointed at the right tables, and the screens stay exactly as they are. A rebuild is the answer only when the data model itself cannot carry the product, and that is a conclusion you should be able to get in writing within days, not a premise you assume on day one.
If this is your app, send the repo.
A 30-minute scope call. We read what is live, name what is dangerous, and quote a fixed scope.
How Techtribe fixes database errors in generated apps
We start from the error, not from the stack. On the scope call you show us the failing action and the exact message; we read the repo and the database together and name the layer the fix belongs to — environment, migration, query, policy, or connection. You leave with what is broken, what it takes to repair it, and a fixed price.
Repairs that stay inside the schema and the queries are quoted as a fixed scope. When the repair needs production architecture — a real migration history, tests around the flows users pay for, monitoring so the next error is visible before a user reports it — that is a Production MVP on your existing repo, seven thousand dollars fixed. Ongoing work on a product that keeps changing is a Dedicated Team. Prices and inclusions are on the packages page.
Bring the live URL, the repo, the exact error text, and one account that reproduces it. Thirty minutes is usually enough to say whether this is a two-hour fix or a scoped project — and if it is smaller than any package, we will tell you that too.
Book a scope call with the error message in hand.
Questions
Why does my vibe-coded app keep throwing database errors?
Usually one of five causes: the client points at the wrong project, a migration never ran, a column was renamed without updating the query, row level security is filtering the rows out, or the connection pool is exhausted. Find the exact error text first; the fix follows from it.
Should I fix the database error or rebuild the app?
Fix it first. Most database errors in AI-generated apps are one migration, one query, or one policy away from working. Rebuild only when the schema itself cannot represent the product's data, which a repair pass will tell you within hours, not weeks.
How do I test that a database fix actually worked?
Test with two real accounts through the same client the app uses, not just the SQL editor. Confirm the failing action now succeeds, confirm the row landed with the right owner, and confirm the fix did not open the table to the anon key.
Can an AI agent fix database errors safely?
It can, if someone reviews the diff and tests the result. Agents often reach for the fastest visible fix — dropping a constraint, disabling a policy, or widening a query — which makes the error disappear and the data problem worse.