How to Fix Broken Supabase RLS Policies in AI-Built Apps
Row Level Security policies generated by AI tools usually have one of three issues. Here is how to find and fix each one.
TL;DR
AI-generated RLS policies usually fail in one of three ways: RLS is not enabled on the table, the policy condition is too permissive (or too restrictive), or the policies are missing for one of INSERT, UPDATE, or DELETE. Check all three.
What is actually happening
Supabase Row Level Security (RLS) is the security layer that decides which rows each user can read, insert, update, or delete. AI tools enable RLS but often write incomplete or wrong policies. The result: either users cannot see their own data, or any user can see everyone's data. Both are emergencies.
The fix, in order
Try these in sequence. Most apps are fixed by the first or second step.
- 1
Confirm RLS is actually enabled on every table
Open the Supabase SQL editor and run: SELECT tablename, rowsecurity FROM pg_tables WHERE schemaname = 'public'; Every table should have rowsecurity = true. If any are false, the table is fully exposed to your anon key and anyone with your URL can read everything. Enable RLS with: ALTER TABLE <tablename> ENABLE ROW LEVEL SECURITY;
- 2
Check that policies exist for every CRUD action
Run: SELECT tablename, policyname, cmd FROM pg_policies WHERE schemaname = 'public'; You should see at least one policy per table for each of SELECT, INSERT, UPDATE, DELETE that your app uses. If a CRUD operation has no policy, RLS blocks it entirely. The error in your app will look like 'new row violates row-level security policy'.
- 3
Read each policy and understand what it allows
Click into each policy in the Supabase dashboard (Database then Policies) and read the USING and WITH CHECK clauses. The most common AI-generated mistakes are: USING (true) which lets everyone see everything, USING (auth.uid() IS NOT NULL) which lets any logged-in user see all rows, and missing WITH CHECK clauses that let users INSERT rows they should not own.
- 4
Write the right policy for the most common pattern
For tables where each row belongs to one user, the correct policies are: SELECT: USING (auth.uid() = user_id), INSERT: WITH CHECK (auth.uid() = user_id), UPDATE: USING (auth.uid() = user_id) WITH CHECK (auth.uid() = user_id), DELETE: USING (auth.uid() = user_id). Replace user_id with whatever column actually holds the owner.
- 5
Test with two different users
Create two test accounts. Sign in as user A and create some records. Sign in as user B and try to read, edit, and delete user A's records. None of those should succeed. If any of them succeed, your RLS is broken and you have a security hole.
When to stop debugging and get help
If your app has multi-tenant logic (organizations, teams, roles), RLS policies get complicated fast. AI tools almost always get them wrong. A real engineer can audit and rewrite the policies in a few hours, which is much cheaper than the breach you avoid.
Stuck after trying these?
We do a $100 Quick Audit that pinpoints exactly what is wrong, why, and what it will take to fix. No sales call gating, no fluff.
Start a $100 AuditRelated fixes
How to Fix Lovable Supabase Connection Timeout Errors
Lovable apps timing out when connecting to Supabase. The actual causes, the fixes that work, and when to stop debugging and switch.
How to Fix Stripe Webhooks Not Firing in Lovable Apps
Lovable Stripe integrations break in production because of a few specific webhook issues. The exact fixes, in order.
How to Fix Lovable Deployment Failures and Build Errors
Your Lovable app builds fine in the editor but fails to deploy. The four most common causes and exactly how to fix each one.
How to Fix Lovable Authentication Breaking in Production
Auth works locally in your Lovable app but breaks the moment users hit the deployed site. Why this happens and how to fix it.
Not sure exactly what is broken? Run a free scan and get a diagnosis in under 2 minutes.