Troubleshooting Supabase RLS: fixing new row violates policy errors
postgres error 42501 · insert blocked by row level security
You call insert() and Supabase answers with new row violates row-level security policy for table "posts". The table is protected, your write isn't allowed by any policy, and Postgres is doing exactly what it was told. Here's the fix, in the order to apply it.
1. Understand what the error means
RLS is enabled on the table, but no INSERT policy has a WITH CHECK expression that evaluates true for the row you're writing. With RLS on and zero matching policies, every write is denied — the default is deny, not allow.
2. Add grants, enable RLS, then write the policy
Order matters. Grants are separate from policies: without them PostgREST returns a permission error instead.
-- 1. grants (PostgREST needs these, RLS alone is not enough)
grant select, insert, update, delete on public.posts to authenticated;
-- 2. enable RLS
alter table public.posts enable row level security;
-- 3. an INSERT policy whose WITH CHECK the new row satisfies
create policy "Users can insert their own posts"
on public.posts
for insert
to authenticated
with check (auth.uid() = user_id);3. Make sure the row satisfies the check
The most common cause isn't the policy — it's the payload. If the policy checks auth.uid() = user_id and the insert omits user_id, the check fails.
// the row must actually carry the column the policy checks
const { data: { user } } = await supabase.auth.getUser();
await supabase.from("posts").insert({
title,
user_id: user.id, // <- omitted user_id is the #1 cause of this error
});4. Confirm the request is authenticated
auth.uid() is null for anonymous requests, so any policy scoped to the current user fails. Check that the user is signed in and that the client sends the session token — a server-side client created without the user's token behaves as anon.
5. Don't "fix" it by disabling RLS
alter table ... disable row level security makes the error go away and makes the table readable and writable by anyone holding your publishable key. That's the single most common data leak we find in AI-built apps.
6. Verify
Sign in as a real user and insert a row; then sign in as a second user and confirm they can't read or modify the first user's rows. If both pass, the policy is scoped correctly.
FAQ
- Why does my insert fail with “new row violates row-level security policy”?
- Row Level Security is enabled on the table but no INSERT policy has a WITH CHECK expression that the new row satisfies — so Postgres rejects the write.
- Does disabling RLS fix it?
- It stops the error but exposes the table to anyone with your anon key. Add a scoped INSERT policy instead of disabling RLS.
- Why do I get a permission error instead of an RLS error?
- Missing table GRANTs. PostgREST needs GRANT INSERT on the table for the authenticated (or anon) role in addition to the policy.
Check your RLS automatically
VibeSafely probes your live app for world-readable tables, missing policies and exposed keys — then hands you copy-paste fixes for Lovable, Bolt and Cursor.
scan my app