LeakPatrol › Guides

Supabase "new row violates row-level security policy": 5 causes and the safe fix

By LeakPatrol · Updated 4 November 2026

If your Lovable, Bolt or Cursor app fails to save with new row violates row-level security policy for table "…" (Postgres error 42501), Row Level Security is doing its job: no policy allows this row to be written by this user. The right fix is a policy that matches what your app does. The fix AI tools often suggest is to turn RLS off, which lets anyone with your public anon key write anything.

Cause 1: there is no INSERT (or UPDATE) policy

With RLS on, everything is denied until a policy allows it. A table with only a select policy can be read but not written. Check what policies exist:

select policyname, cmd, roles, qual, with_check
from pg_policies where tablename = 'notes';

If there is no row with cmd = INSERT (or ALL), add one:

create policy "Users add their own notes"
on public.notes for insert
to authenticated
with check (auth.uid() = user_id);

Cause 2: the row's user_id is empty or someone else's

The policy above only passes when the row's user_id equals the signed-in user. AI-written code often forgets to send user_id, so it arrives as null and the check fails. Let the database fill it in instead of trusting the browser:

alter table public.notes
alter column user_id set default auth.uid();

Cause 3: the user isn't actually signed in

If the request runs before the session loads, or the user logged out, it runs as anon, and a policy to authenticated doesn't apply. Log (await supabase.auth.getSession()).data.session right before the insert. If it's null, fix the sign-in flow, not the policy.

Cause 4: insert().select() or upsert needs more than an insert policy

.insert(...).select() reads the new row back, so it also needs a select policy that lets the user see it. .upsert(...) can update an existing row, so it needs insert, update and select policies. If the plain insert works but the version with .select() fails, this is your cause.

Cause 5: file uploads to Supabase Storage

If the error appears when uploading a file, the table is storage.objects. Add a policy that lets users upload only into their own folder:

create policy "Users upload to their own folder"
on storage.objects for insert
to authenticated
with check (
  bucket_id = 'avatars'
  and (storage.foldername(name))[1] = auth.uid()::text
);

Then upload to a path that starts with the user's id, like `${user.id}/avatar.png`.

The fixes that leave your data open

Each of these makes the error go away and lets anyone with your anon key, which is in your app's JavaScript, write to the table:

A prompt to paste into Lovable, Bolt or Cursor

Saving fails with "new row violates row-level security policy".
Fix it without weakening security:
1. Show me the current policies on this table (pg_policies).
2. Add only the missing policy for what the code does (insert, update,
   select after insert, or upsert), for the authenticated role, with
   auth.uid() = user_id in both USING and WITH CHECK.
3. Set user_id to default auth.uid() instead of sending it from the browser.
4. Do not disable RLS, do not use true in a policy on user data, do not
   add policies for anon, and do not use the service_role key in the frontend.
Show me the SQL before running it.

Not sure what else is open? LeakPatrol's free scan checks your live app from the outside for leaked keys and public files in about a minute, and our RLS policy generator writes the policies for common table types.

Check your app in a minute

Free, read-only, no sign-up. Every problem comes with a plain-English fix and a prompt for your AI tool.

Get the 10-point checklist

Every check we run on AI-built apps, with how to do each one yourself. Straight to your inbox.

Questions

What does "new row violates row-level security policy" mean in Supabase?

Row Level Security is on for the table and no policy allows the current user to write that row. Usually there is no insert or update policy, the row's user_id does not match the signed-in user, or the request runs as anon because the user is not signed in.

Should I disable RLS to fix it?

No. With RLS off, anyone who has your public anon key, which is in your app's JavaScript, can read and change every row. Add the missing policy instead.

Why does insert work but insert().select() fail?

insert().select() reads the new row back, so it also needs a select policy that lets the user see the row. upsert additionally needs an update policy.

How do I fix it for Supabase Storage uploads?

Add an insert policy on storage.objects for the authenticated role that checks the bucket and that the first folder in the file path equals auth.uid(), then upload files under the user's id.

More guides