afterbuild/ops
ERR-945/Supabase · RLS
ERR-945
new row violates row-level security policy for table (SQLSTATE 42501)

appears when:After RLS is enabled on a table but no INSERT policy has been written for the authenticated role

Supabase RLS blocking insert

Postgres defaults to deny when RLS is on. Every INSERT, SELECT, UPDATE, DELETE is blocked until you write a policy.

Last updated 17 April 2026 · 6 min read · By Hyder Shah
Direct answer
Supabase RLS blocking insert (42501) means the target table has RLS enabled and the authenticated role has no INSERT policy whose WITH CHECK evaluates true for the row. Create a policy FOR INSERT TO authenticated WITH CHECK (auth.uid() = user_id) and a matching SELECT policy so the chained .select() can read the row back.

Quick fix for Supabase RLS blocking insert

supabase/migrations/insert_policy.sql
sql
01-- Single-tenant ownership pattern (most AI apps start here)02create policy "users insert own rows"03on public.tasks04for insert05to authenticated06with check (auth.uid() = user_id);07 08-- Default auth.uid() so the client cannot forget to set user_id09alter table public.tasks10alter column user_id set default auth.uid();11 12-- Verify in SQL editor13set role authenticated;14insert into public.tasks (title) values ('test') returning *;
Run in Supabase SQL editor — creates the INSERT policy, sets the default, and verifies with a test insert

Deeper fixes when the quick fix fails

01 · Add the matching SELECT policy

supabase/migrations/select_policy.sql
sql
01create policy "users read own rows"02on public.tasks03for select04to authenticated05using (auth.uid() = user_id);
Without a SELECT policy the chained .select() returns nothing → PGRST116 on the client

02 · Multi-tenant: join through memberships

supabase/migrations/multi_tenant_policy.sql
sql
01create policy "members insert org rows"02on public.tasks03for insert04to authenticated05with check (06  organization_id in (07    select organization_id from memberships08    where user_id = auth.uid()09  )10);

03 · Write a pgTAP regression test

tests/rls_tasks.sql
sql
01-- tests/rls_tasks.sql — asserts cross-tenant writes fail02begin;03select plan(1);04 05set local role authenticated;06set local "request.jwt.claims" = '{"sub":"user-a-uuid"}';07 08select throws_ok(09  $$insert into public.tasks (user_id, title) values ('user-b-uuid', 'test')$$,10  '42501',11  'cross-user insert must be blocked by RLS'12);13 14select * from finish();15rollback;
Add to CI so the policy cannot regress when someone edits the schema

Why AI-built apps hit Supabase RLS blocking insert

Lovable, Bolt, Base44, and Cursor all ship apps that talk to Supabase. In preview the generated code uses the service_role key, a superuser role that bypasses RLS entirely. Every operation succeeds regardless of policies because the database never evaluates them. The preview gives the model no feedback that policies are missing, so the model never writes them. The app appears complete.

At deploy the client switches to the anon key or the authenticated role — non-privileged roles that trigger RLS. Postgres default when RLS is enabled and no policy matches is deny. Your INSERT returns SQLSTATE 42501 if the client used .insert() alone, or returns a 200 with an empty array if the INSERT is chained to .select() and RLS also blocks the follow-up read. Either failure is indistinguishable from an app bug if you do not know to check the Postgres log.

The third variant is an INSERT policy that exists but has the wrong WITH CHECK. A common AI-generated mistake is a policy whose USING matches the user but whose WITH CHECK is missing or set to true. Postgres evaluates only WITH CHECK for INSERT; the USING clause is ignored. The policy looks written, the table looks protected, inserts still fail or silently succeed for unrelated rows.

Supabase RLS blocking insert by AI builder

How often each AI builder ships this error and the pattern that produces it.

AI builder × Supabase RLS blocking insert
BuilderFrequencyPattern
LovableEvery Supabase scaffoldUses service_role in preview; ships with no policies
Bolt.newCommonWrites USING instead of WITH CHECK on INSERT
Base44CommonFOR ALL WITH CHECK (true) — identical to no protection
CursorSometimesLeaves user_id nullable with no default auth.uid()
Replit AgentRareTargets policies at `public` role — includes anon

Related errors we fix

Stop Supabase RLS blocking insert recurring in AI-built apps

Still stuck with Supabase RLS blocking insert?

Emergency triage · $299 · 48h turnaround
We restore service and write the root-cause report.
start the triage →

Supabase RLS blocking insert questions

What does Postgres error 42501 mean on an INSERT?+
42501 is the Postgres permission_denied SQLSTATE. On Supabase it fires when Row Level Security is enabled on the target table but no INSERT policy exists, or the policy's WITH CHECK evaluates to false for the row you are trying to write. The anon or authenticated role cannot write the row. Fix by writing a policy whose WITH CHECK matches the ownership column.
Why does my INSERT return PGRST116 instead of 42501?+
PGRST116 is a PostgREST error meaning zero rows were returned when the client expected one. It surfaces when INSERT chained .single() or .select().single() but RLS filtered the returned row. The row may have been written and hidden by a missing SELECT policy, or the INSERT itself was blocked. Check Postgres logs for the underlying 42501 to know which.
Does enabling RLS without policies block all inserts?+
Yes. Postgres default behavior is deny. The moment you enable RLS on a table with no policy, every operation from every non-superuser role is blocked. Supabase anon and authenticated roles are not superusers. service_role bypasses RLS, which is why inserts work from the server but fail from the browser. Add a policy for every operation you need: SELECT, INSERT, UPDATE, DELETE.
What is the difference between USING and WITH CHECK in an INSERT policy?+
USING filters which existing rows are visible or modifiable. WITH CHECK validates rows being written. For INSERT only WITH CHECK applies because there is no existing row to filter. For UPDATE both apply. A correct INSERT policy has only WITH CHECK; a correct UPDATE policy usually has both.
How long does a full Supabase RLS audit take?+
For under 20 tables and single-tenant ownership, a complete audit including policy writes, pgTAP test suite, and cross-tenant verification takes about 1 hour. Multi-tenant apps with organization membership joins take 2-4 hours. Our Security Audit service is fixed-fee at $499 and includes policy writes plus a CI test suite so policies cannot regress silently.
Next step

Ship the fix. Keep the fix.

Emergency Triage restores service in 48 hours. Break the Fix Loop rebuilds CI so this error cannot ship again.

About the author

Hyder Shah leads Afterbuild Labs, shipping production rescues for apps built in Lovable, Bolt.new, Cursor, Replit, v0, and Base44. our rescue methodology.

Sources