'new row violates row-level security policy' in Supabase (403 / error 42501)
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
- 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.
- 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.
- 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.
- 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.
- 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.
- 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.
If your AI-built app uses Supabase, Row Level Security is the actual fix for exposed data, and it is free. The dashboard's Security Advisor lists every table you left open.
Try Supabase →Drop-in authentication that handles sessions, tokens, and protected routes properly, so you stop shipping the hand-rolled auth an AI guessed at. Generous free tier for a real app.
Try Clerk →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
- Supabase troubleshooting: Storage 403 'new row violates row-level security policy' on upload
- Supabase GitHub discussion #6765
- Supabase GitHub discussion #30510
- Niko Fischer: Supabase 'row level security policy' error explained
- Supabase docs: Storage access control
Related errors
Lovable Supabase RLS not enabled: anyone can read your app's data (CVE-2025-48757)
Supabase Row Level Security is off: your whole database is readable with the anon key
Bolt.new + Supabase: 403 Forbidden, data not saving, or the wrong project connected
'Edge Function returned a non-2xx status code' in Supabase and Lovable
Lovable stuck in an error loop: 'Try to Fix' keeps failing
The Vibe Oops briefing
One email when something ships to production that should not have. New incidents, new error guides, no filler.