Home / Errors / Supabase / rls-policy-violation-42501

'new row violates row-level security policy' in Supabase (403 / error 42501)

Supabase Moderate 15-45 minutes Last verified Jul 17, 2026
The fix  The error means RLS is on but no policy allows the write. Add an INSERT policy with a WITH CHECK clause that matches the row, confirm the client is actually authenticated (auth.uid() returns null on anonymous requests), and for Storage uploads add a SELECT policy on storage.objects alongside INSERT, because the upload does INSERT ... RETURNING * and needs read access.

Symptoms

  • Form submissions fail with a 403 and 'new row violates row-level security policy for table ...'
  • The response carries Postgres error code 42501
  • Supabase Storage uploads fail with the same message even though an INSERT policy exists on the bucket
  • The UI sometimes shows success while the Network tab shows a 403 on /rest/v1/ requests and nothing is actually saved

Why it happens

No INSERT policy with a matching WITH CHECK clause

RLS is enabled but there is no policy permitting the write, or the existing policy's WITH CHECK condition does not match the row being inserted.

The client is unauthenticated

If the session is not passed with the request, auth.uid() evaluates to null inside the policy and every comparison fails, so the insert is rejected.

Storage uploads need SELECT, not just INSERT

The Storage API performs INSERT ... RETURNING *, so a SELECT policy on storage.objects is required in addition to the INSERT policy. Missing SELECT makes the upload fail with this exact error.

The policy compares auth.uid() to the wrong column

A policy that checks the wrong ownership column rejects perfectly valid rows, which looks identical to a missing policy from the client side.

How to fix it

  1. Check that a policy exists for the exact operationOpen the failing table's Policies tab (Authentication > Policies) in Supabase and confirm a policy exists for the operation the request performs. INSERT policies need a WITH CHECK clause, not USING; a SELECT-only policy does nothing for a write.
  2. Verify the request is actually authenticatedLog supabase.auth.getUser() in the client immediately before the write. If it returns null, the session is not attached to the request, and auth.uid() inside your policy evaluates to NULL, which fails every comparison.
  3. Add the insert policy for user-owned tablesRun: CREATE POLICY "insert own rows" ON public.your_table FOR INSERT TO authenticated WITH CHECK ((select auth.uid()) = user_id); Also make sure the inserted row actually sets user_id to the current user. The policy compares columns; it does not fill them in.
  4. For Storage uploads, add SELECT alongside INSERTThe Storage API performs INSERT ... RETURNING *, so an upload needs read access too. On storage.objects create both an INSERT policy (WITH CHECK bucket_id = 'your-bucket' plus your ownership condition) and a mirroring SELECT policy. A missing SELECT policy fails uploads with this exact error even when the INSERT policy looks correct.
  5. Reproduce it in the SQL editor with role impersonationRun the exact failing statement in the Supabase SQL editor while impersonating a role, using the editor's role/impersonation feature. This shows which policy clause rejects the row instead of leaving you guessing from the client side.
  6. In Lovable, hand the AI the full errorPaste the complete 42501 message into Chat mode and ask Lovable to write the missing policy. Do not click 'Try to Fix' repeatedly; RLS policy problems are exactly the kind of non-log-visible issue that sends it into a loop.

How to prevent it

  • Whenever the AI creates a table, ask for policies covering all four operations, not just SELECT.
  • Test every write path with a signed-in, non-admin account before shipping.
  • Remember Storage buckets: uploads need both INSERT and SELECT policies on storage.objects.
  • Watch the Network tab, not just the UI; a 403 on /rest/v1/ can hide behind an optimistic success toast.

Tools that actually fix this

Recommended because they address the failure mode above, not because of the payout. Some are affiliate links; see how we choose.

Frequently asked questions

What does Supabase error 42501 mean?

Postgres error 42501, surfaced by Supabase as 'new row violates row-level security policy', means Row Level Security is enabled on the table but no policy permits the operation your request is performing. It is the mirror image of the RLS-off problem: the lock works, but nobody wrote you a key. The fix is adding or correcting the policy for that exact operation, usually an INSERT policy with a WITH CHECK clause.

Why does my Supabase Storage upload fail with 'new row violates row-level security policy' when I have an INSERT policy?

Supabase Storage uploads run INSERT ... RETURNING *, so the request needs SELECT access on storage.objects in addition to INSERT. If you only created an INSERT policy for the bucket, the RETURNING clause fails the read and the whole upload dies with the RLS error. Add a SELECT policy that mirrors your INSERT policy's bucket and ownership conditions.

Why is auth.uid() null in my Supabase policy?

auth.uid() returns null when the request reaches Supabase without an attached session, which happens when the client is not signed in or the auth token is not passed with the call. A null auth.uid() fails every equality check in your policy, so inserts are rejected with 42501 even if the policy itself is written correctly. Log supabase.auth.getUser() right before the write to confirm the session exists.

What is the difference between USING and WITH CHECK in a Supabase policy?

USING filters which existing rows an operation can see or touch, while WITH CHECK validates the new row being written. INSERT policies need WITH CHECK, not USING, because there is no existing row to filter yet. A policy created for the wrong operation, or without the WITH CHECK clause it needs, leaves writes failing with 'new row violates row-level security policy'.

Sources

Related errors

The Vibe Oops briefing

One email when something ships to production that should not have. New incidents, new error guides, no filler.