UnbreakMyApp / Fix guides

Supabase error: "new row violates row-level security policy"

Your form or save button fails with a 403 / 42501 RLS error. Why Supabase blocks the insert and how to write a policy that lets the right users save data.

Why it happens

Row Level Security (RLS) is on for the table, which is good, but there is no INSERT policy that matches the request. With RLS on and no matching policy, Postgres rejects every new row with error 42501: "new row violates row-level security policy for table ...".

In AI-built apps the usual culprits are: the policy only covers SELECT, the policy checks auth.uid() = user_id but the app never sends user_id (or sends a different value), or the user is not actually logged in when the insert runs, so the request uses the anonymous role.

Upserts and inserts that return data (.insert().select()) also need a SELECT policy, so a missing SELECT policy can make an insert look like it failed.

How to fix it, step by step

  1. In Supabase → Table Editor, open the table and check that RLS is enabled. Do not turn it off to "fix" the error; that makes your data public.
  2. Go to Authentication → Policies and look at the policies for that table. Note which commands (SELECT, INSERT, UPDATE, DELETE) and roles (anon, authenticated) each covers.
  3. Add an INSERT policy for the authenticated role with a WITH CHECK such as (auth.uid() = user_id). For public forms (like a waitlist), allow INSERT for anon only, with no SELECT.
  4. Make sure the app sends user_id equal to the logged-in user's id, or set the column default to auth.uid() so the database fills it in.
  5. If your code does .insert(...).select(), add a matching SELECT policy, or remove .select() if you do not need the row back.
  6. Test while logged in, and again while logged out, to confirm only the intended users can insert.

Paste-ready prompt for Lovable

Saving to the "<table>" table fails with "new row violates row-level security policy". Please:
1. Keep RLS enabled. Do not disable it.
2. Show me the current policies on this table.
3. Write a migration that adds an INSERT policy for the authenticated role: WITH CHECK (auth.uid() = user_id).
4. Set user_id to default auth.uid() so the client does not need to send it.
5. If the insert code uses .select() afterward, add a SELECT policy using (auth.uid() = user_id).
6. Make sure the insert only runs after the user session has loaded.
Explain each policy in plain English before applying.

FAQ

Can I just turn RLS off?

No. With RLS off, anyone with your public key can read and change every row in that table. Add the right policy instead.

Why does it work in the SQL editor but not in my app?

The SQL editor runs as an admin role that bypasses RLS. Your app runs as anon or authenticated, so policies apply.

Still broken?

Get a free diagnosis by email within 24 hours. Fixed or refunded.

Get a free diagnosis

Or get the weekly fix digest:

📬 This Week in Broken Apps: the most common Lovable/Supabase breaks and how to fix them. Free, weekly, unsubscribe anytime.

Free: the Vibe-Code Fix Checklist.

Sources