VVibeFix

Guide

New row violates row-level security policy (Supabase): why data won't save and how to fix it

Updated

Short answer: "New row violates row-level security policy" means the database is blocking a write it has no rule for. The standard fix, from the Supabase and PostgreSQL docs, is an INSERT policy that lets the signed-in person write their own row, with their user id on it.

Get it fixed: a free check first confirms the problem on your live app, usually in 4 to 10 minutes.

Free. No account needed. It usually takes 4 to 10 minutes.

Want it fixed? Our AI agent fixes it in your code and tests your live app again. See pricing.

Login, emails or payments broke in your live app? Our AI agent fixes it in your code, then tests your live app again.

In our checks of 44 live AI-built apps, 7 (16%) had a button or form that does nothing or does not save.

Does a brand-new account's first save go through? Tell us what broke. Our AI agent tries it on your live app and sends you a screenshot, free. Check my app free

What we add from real checks: the save usually works for the owner and fails for a brand-new account on the live address, because the owner tests signed in, often in the preview, with rows that already exist. The person sees a Save button that does nothing; the error only shows in the console or logs. A public example: new row violates row-level security policy for table "players_secure" (supabase/postgres #1708).

In practice that is an INSERT policy, plus a SELECT policy if the app reads the new row back. Do not switch row-level security off.

What does "new row violates row-level security policy" mean in Supabase?

It means row-level security is on and no policy lets this person write this row, so the database blocks the write as designed.

Row-level security (RLS) is a set of rules on each table about who may read or write which rows. Supabase says that once RLS is enabled, "no data is accessible through the API when using a publishable key, until you create policies." PostgreSQL says rows being inserted that do not pass the policy "will result in a policy violation error, and the entire INSERT command will be aborted."

So the fix is a policy that lets the right person write the right row, not switching RLS off.

What causes "new row violates row-level security policy", and how do I fix each cause?

The causes are a missing INSERT policy, a row that does not carry the signed-in user's id, a person who is not signed in, a missing SELECT policy when the app asks for the new row back, an upsert or update, and a file upload to Supabase Storage.

You can add policies in the Supabase SQL editor. Replace the table and column names with yours.

1. Why does the save fail when the table has no INSERT policy?

A table may have a policy for reading but none for adding rows, and without an INSERT policy every new row is refused.

How to fix it

  1. Open the table in the Supabase dashboard and look at its policies.
  2. Add an insert policy for signed-in users that checks the row belongs to them, adapted from Supabase's profiles example: create policy "Users can create their own rows" on public.your_table for insert to authenticated with check ( (select auth.uid()) = user_id );
  3. Save again from the app while signed in.

Source: Supabase: Row Level Security

2. Why does the save fail when the row does not carry the signed-in user's id?

The policy compares a column (such as user_id) with the signed-in user, and the check fails if the app leaves that column empty or fills it with another id.

How to fix it

  1. In the code that saves, set user_id to the id of the signed-in user from the current session.
  2. Make sure the column name in the policy matches the column the app fills.

Source: Supabase: Row Level Security

3. Why does the save fail when the person is not signed in?

Supabase says that when a request has no authenticated user, auth.uid() returns null, so a policy like auth.uid() = user_id silently fails for them. It recommends auth.uid() IS NOT NULL AND auth.uid() = user_id. This happens when a form is used before sign-in, or when the session has expired.

How to fix it

  1. Require sign-in before the screen that saves, or wait until the session exists.
  2. If the form is meant for visitors who are not signed in (a contact form, for example), write a separate insert policy for the anon role that allows only that table and only the fields it needs. See the contact form case.

Source: Supabase: Row Level Security

4. Why does the save fail when the app asks for the new row back and there is no SELECT policy?

PostgreSQL says that if an insert returns the new row, "any newly inserted or updated rows from the relation must satisfy the relation's SELECT policies", and otherwise an error is thrown. In supabase-js this is an insert followed by .select(), which asks for the new row back.

How to fix it

  1. Add a select policy so people can read their own rows: create policy "Users can view their own rows" on public.your_table for select to authenticated using ( (select auth.uid()) = user_id );
  2. Or, if the app does not need the row back, stop asking for it.

Source: PostgreSQL: CREATE POLICY, Supabase: Row Level Security

5. Why does an upsert or an update fail with this error?

An upsert (insert, or update if it exists) needs more than an insert policy. PostgreSQL checks the SELECT policies for an insert with ON CONFLICT DO UPDATE, and the UPDATE policy applies to its update part. Supabase also warns that without a SELECT policy, an UPDATE "will not work as expected."

Get it fixed: a free check first confirms the problem on your live app, usually in 4 to 10 minutes.

Free. No account needed. It usually takes 4 to 10 minutes.

Want it fixed? Our AI agent fixes it in your code and tests your live app again. See pricing.

How to fix it

  1. Add an update policy for the owner of the row, with both using and with check comparing (select auth.uid()) with user_id.
  2. Make sure a select policy exists too (cause 4).
  3. More detail: edits or upserts blocked by row-level security.

Source: PostgreSQL: CREATE POLICY, Supabase: Row Level Security

6. Why does a file upload to Supabase Storage fail with this error?

Uploads are rows in the storage.objects table. Supabase says "by default Storage does not allow any uploads to buckets without RLS policies", and that the only policy needed for uploading is INSERT on storage.objects. Overwriting with upsert also needs SELECT and UPDATE.

How to fix it

  1. Add an insert policy on storage.objects for the authenticated role, limited to your bucket (Supabase's example checks bucket_id and the folder with storage.foldername(name)).
  2. If the app overwrites files, add select and update policies as well.

Source: Supabase Storage: Access control

App broke after you published it? Our AI agent fixes it in your code, then tests your live app again so you see the result yourself. The first check is free. Check my app free

How do I check it's fixed?

  1. On the live address (not the preview), sign up as a brand-new account and save a first row. Reload the page: the row is still there.
  2. Sign up as a second new account. It can save its own rows and cannot see the first account's.
  3. The browser console and your Supabase logs show no "violates row-level security policy" error for those saves.

What is the main catch when fixing "new row violates row-level security policy"?

The main catch is that switching row-level security off, or putting the secret or service_role key in the app, makes the error disappear and opens the table to anyone. Supabase says to never use a secret key in the browser. The fix is the missing policy, not less security.

Why does saving work for me but fail for a new account?

You test as the owner, often from the builder and signed in, with rows that already exist, while a brand-new account or a visitor who is not signed in is governed by rules you never tried. You're signed in, your customers aren't.

Frequently asked questions

Can I just turn row-level security off?

The error disappears because the table becomes open to anyone with your public key, which is the opposite of what you want. Supabase also says never to use the secret or service_role key in the browser to get around it, because it bypasses RLS. Write the missing policy instead. See users can see other users' data.

The same insert works in the SQL editor but fails from the app. Why?

A public report (supabase/postgres #1708, unanswered) describes a SQL insert working while the same insert fails from the app. The app sends requests as the anon or authenticated role, and Supabase's policies are written for those roles, so test policies as the role your app uses.

I added an insert policy and it still fails. What else?

Check causes 2 to 4: the row's user_id must equal the signed-in user, the person must be signed in, and if the code reads the row back, a select policy is needed.

Can VibeFix fix it for me?

Yes, if you ask. The free check confirms the problem first. Our AI agent can find the cause, fix it in your code and test your live app again. The price is on the pricing page.

Related guides

Sources (official documentation)

Fixed it? Prove it works on your live app

After the fix, the proof is a brand-new account saving its first row on your live app, and not seeing anyone else's. Your own account cannot show that, because it is not new. Your AI fixed it. VibeFix proves it works on the live site.

VibeFix's free check tries the problem you describe on your live app: tell us what broke, and our AI agent tries it and sends you a screenshot. If you want it fixed, it finds the cause, fixes it in your code and re-checks your live app. The free check never changes your app and never emails your users. Check my app free

Related: Supabase RLS policy generator · Fix 42501 tool

VibeFix is independent. Supabase, Lovable, Bolt and Base44 are trademarks of their owners.

Want it fixed?

Our AI agent fixes it in your code and tests your live app again. See pricing.

It starts with a free check that confirms the problem on your live app, no account needed.

Check my app free

More guides