Home / Fix guides
LovableBoltSupabase

Fix "new row violates row-level security policy" in Lovable and Supabase apps

Updated 2026-11-11

new row violates row-level security policy for table "…" (code 42501)

What you see

Your app worked in preview. Then you (or the AI builder) turned on Row Level Security, or you deployed and signed in as a real user, and every insert fails:

new row violates row-level security policy for table "todos"

The Supabase client returns this with Postgres error code 42501.

Why it happens

With RLS enabled, Postgres refuses every row that no policy allows. For inserts, the deciding rule is the policy’s with check expression. There are three common causes, all covered in Supabase’s RLS docs:

  1. No grant: the role (anon or authenticated) has no insert privilege on the table. This fails before any policy runs.
  2. No insert policy, or a with check that the new row doesn’t satisfy, such as a user_id that isn’t the signed-in user.
  3. Not signed in: auth.uid() returns null when the request has no valid session, and null = user_id is never true.

AI-generated code often hits #2 by never sending user_id, or hits #3 by inserting before the session has loaded.

Fix it

1. Make sure the row carries the owner. Either send it from the client:

const { data: { user } } = await supabase.auth.getUser();
await supabase.from('todos').insert({ title, user_id: user.id });

or let the database fill it in with a column default:

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

2. Add an insert policy scoped to signed-in users, plus the select policy (updates won’t work without one):

alter table public.todos enable row level security;

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

create policy "Users can view their own todos"
on public.todos for select
to authenticated
using ( (select auth.uid()) = user_id );

3. Check the grants. If the error persists, confirm authenticated can insert:

grant select, insert, update, delete on table public.todos to authenticated;

4. Confirm a session exists before the insert runs. Call supabase.auth.getUser() first, and redirect to login if it returns no user.

Don’t do this

Check it worked

Sign in as user A and insert a row: it should succeed. Sign in as user B and try to read it: you should get no rows back, not an error. Supabase recommends locking this in with supabase test db (pgTAP) tests for each table.

Sources

Tools mentioned

Database

Supabase

Hosted Postgres with auth, storage, realtime and edge functions in one project. It is the backend Lovable and many Bolt apps connect to.

Free: 500 MB database, 1 GB file storage, 5 GB egress
Auth

Supabase Auth

The auth service built into every Supabase project: email/password, magic links, OAuth providers and anonymous sign-ins, tied directly to Row Level Security.

Free: 50,000 monthly active users on the Free plan