Your AI-built app works in the builder but breaks on deploy with a Supabase "permission denied" error. Here is why, and how to fix it.
You built an app in Lovable, Bolt, v0, or Cursor. It worked perfectly in the preview. You deployed it, opened the live URL, and now half the screen is empty or you see something like:
permission denied for table profiles
code: 42501
or your data loads as null and the console shows getUser() returned nothing. The frustrating part is that nothing changed in your code between the working preview and the broken deploy. So what happened?
Why this happens in almost every AI-built app
This is one of the most common failures we see, and it is not random. AI app builders generate a working front end fast, but they often leave the security boundary between your app and your database half finished. Two things are usually true at once:
- Row Level Security (RLS) is on. Supabase enables RLS so a table can only be read or written by the right user. That is correct and you want it. Your policy probably says something like "a row is visible when
auth.uid() = user_id". - The request reaching Supabase in production is anonymous. In the builder preview, the request often carried your session, or ran with elevated access, so RLS let it through. In the real deploy, the server is calling Supabase without the logged-in user's identity attached. So
auth.uid()isnull, the policy does not match, and Supabase correctly refuses with42501 permission denied.
In plain terms: your security rules are working, but your app is knocking on the door without showing its ID.
How to confirm this is your problem
Before changing anything, keep the exact error text. Then check, in order:
- Is RLS enabled on the failing table? In Supabase, open the table and look at its policies. If RLS is on and there is no policy that matches your query, every read or write fails.
- Is the request authenticated on the server? Add a log on the server route that runs the query and print whether it has a user. If it prints
nulloranonymous, that is the cause. - Do your environment variables point at the same Supabase project that failed? A deploy pointed at the wrong project, or missing
SUPABASE_URLand the anon key, produces the same symptom. - Do your auth redirect URLs include the live domain? In Supabase Auth settings, the site URL and redirect allow-list must include your production domain, not just
localhost.
The fix
The real fix is to make the request carry the user's identity, so auth.uid() is populated when RLS checks it. In a Next.js and Supabase app that usually means:
- Use a request-scoped server client that reads the auth cookie, instead of a plain anonymous client, on any server route or action that touches a protected table.
- Refresh the session in middleware so the cookie stays valid across navigation and after a hard refresh on the production domain.
- Confirm the RLS policy matches your real access pattern. If users should see their own rows, the policy compares
auth.uid()to the row'susercolumn. Do not "fix" this by turning RLS off. - Add the production domain to Supabase Auth redirect URLs, then retest the full path: log in, load the page, and hard refresh, all on the live URL. Then rerun the exact user action that failed and confirm it works against the same Supabase project and domain that broke.
A word of caution
The tempting shortcut is to disable RLS or to use the service role key in the browser so the error disappears. Please do not. Disabling RLS makes every row in that table readable and writable by anyone on the internet, and putting the service role key in client code hands a stranger full control of your database. The error is annoying, but it is protecting you.
If you are stuck, or you are not the technical one
If you are non-technical and this already reads like another language, that is normal. This is exactly the kind of thing an AI builder cannot finish for you, because it lives in the boundary between auth, the database, and the deploy.
We run a service for this called Vibe-Code Rescue. You paste the error and get a free, plain-language diagnosis of what is wrong and what it takes to fix. If you want it fixed, it is a flat rate, you pay only after the fix is verified working, and you keep clean code you own. No subscription, no retainer.
Free diagnosis here: https://rescue.ticassociation.com
A TIC Association creation.
Comments
No comments yet. Start the discussion.