What This Error Means
The Supabase error new row violates row-level security policy means your INSERT (or UPDATE) was blocked because no Row-Level Security policy allowed it. RLS is enabled on the table, but no policy's WITH CHECK condition passes for the row you're trying to write.
In short: RLS defaults to deny. Enable it without a matching INSERT policy and every write fails with this exact message.
The Most Common Causes
- RLS is on, but there's no INSERT policy. Enabling RLS blocks everything until you add explicit policies.
- The
WITH CHECKcondition fails. A policy exists, but the row's values don't satisfy it — usuallyuser_idnot matchingauth.uid(). auth.uid()is null. The request isn't authenticated, so any policy comparing toauth.uid()fails.- You're inserting a
user_idthat isn't the current user. The policy correctly rejects it.
How to Fix It
1. Add an INSERT policy
Most tables holding per-user data need a policy like this:
alter table posts enable row level security;
create policy "Users can insert their own posts"
on posts for insert
to authenticated
with check (auth.uid() = user_id);
The with check clause is what INSERT and UPDATE validate against. If it evaluates to false, you get the error.
2. Set user_id on insert
Because the policy checks auth.uid() = user_id, the row must include the current user's id:
const { data: { user } } = await supabase.auth.getUser();
await supabase.from('posts').insert({
title: 'Hello',
user_id: user.id, // must match auth.uid()
});
Better yet, set it at the database level with a column default of auth.uid() so the client can't get it wrong.
3. Confirm the request is authenticated
If auth.uid() is null, you're calling Supabase without a valid session. Make sure the user is logged in and you're using the authenticated client — see our guide on connecting Supabase to Next.js.
4. Don't test only in the SQL editor
The Supabase SQL editor runs as a superuser and bypasses RLS, so your insert "works" there while real users still fail. Always test as an authenticated user through your app.
How to Prevent It
- Add policies in the same migration where you enable RLS.
- Default
user_idtoauth.uid()at the database level. - Write separate, explicit policies for SELECT, INSERT, UPDATE, and DELETE.
Frequently Asked Questions
Why does the insert work in the SQL editor but fail from my app?The SQL editor runs as the postgres superuser and bypasses RLS. Your app uses the anon or authenticated role, which RLS actually enforces.
Do I need a SELECT policy too?If you return the inserted row with .select(), yes — a SELECT policy must allow reading it back, or the call still errors.
USING filters which existing rows a query can see or affect; WITH CHECK validates the values of new or updated rows.
Can I temporarily disable RLS to debug?Yes, alter table posts disable row level security; — but only in development. Never ship a production table with RLS off.
Need Supabase Set Up Properly?
RLS bugs are usually a sign the auth and data layer needs a careful once-over. At Amex Technology, we design secure Supabase schemas, policies, and auth flows that just work. Explore our development services or get in touch.
Related Services
Need help building this?
Our team specializes in exactly this kind of work. Get a free quote and honest assessment within 24 hours.
Start a Project