Skip to content

Supabase database errornew row violates row-level security policy for table

Row-level security refused the insert. Here’s which policy is missing or failing, how to fix it without turning RLS off, and how to hand it over at a fixed price.

Short answer

The table has RLS on and no INSERT policy that this row passes. Add one whose WITH CHECK matches the row, usually user_id = auth.uid(), and make sure the insert sets user_id for a signed-in user. If you call .select() after the insert, or use upsert, the table also needs a SELECT policy the new row passes. Don’t fix it by turning RLS off.

The usual causes, and the fix

  1. 01

    There is no INSERT policy

    With RLS on, every operation needs its own policy. A SELECT policy lets users read rows but doesn’t let them add any. Add an INSERT policy with a WITH CHECK clause that ties the new row to the user.

    create policy "Users add their own rows"
    on profiles for insert to authenticated
    with check ((select auth.uid()) = user_id);
  2. 02

    The new row fails the WITH CHECK

    The policy exists, but this row doesn’t pass it. Usually the insert leaves user_id empty, sends a different ID, or runs before sign-in finishes, so the request runs as a signed-out user. Set user_id from the signed-in session, or give the column a default so the database fills it in.

    alter table profiles alter column user_id set default auth.uid();
  3. 03

    .select() after the insert, or upsert

    “If a newly inserted or updated row does not satisfy the relation's SELECT policies, an error will be thrown” PostgreSQL docs, CREATE POLICY

    Calling .select() after .insert() asks Postgres to return the new row, so the row must also pass a SELECT policy. Upsert needs a SELECT policy and, to overwrite existing rows, an UPDATE policy too.

  4. 04

    Uploading a file to Storage

    Supabase Storage keeps files in the storage.objects table, which has RLS on. An upload needs an INSERT policy on storage.objects for that bucket; overwriting a file with upsert also needs SELECT and UPDATE policies.

    create policy "Users upload to their own folder"
    on storage.objects for insert to authenticated
    with check (
      bucket_id = 'avatars'
      and (storage.foldername(name))[1] = (select auth.uid())::text
    );

Checked against the vendor’s docs on : Supabase docs: Row Level Security; Supabase docs: Storage access control; PostgreSQL docs: CREATE POLICY.

Rather hand it over?

Fixed scope, fixed price, paid upfront. The work happens in writing; no call needed.

Related: Free Supabase RLS policy checker · Supabase RLS fixes · “Permission denied” on new tables

Questions

More on this error

Can I just turn RLS off, or use the service role key?

No. With RLS off the table has no row rules at all, so anyone holding your public anon key can read and change every row the role has a grant for. The service role key skips RLS entirely, so it belongs on a server only, never in the browser or the app. Add the policy the insert needs instead.

Is this the same as “permission denied for table”?

No. “Permission denied” means the role has no grant on the table at all, and it fails before any policy runs. “New row violates row-level security policy” means the grant is there but a policy refused this row.

The insert works but reading it back fails. Why?

Reading needs its own SELECT policy. With RLS on, rows the user can’t see are left out of the results without an error, so a missing SELECT policy shows up as an empty list rather than as this error.

Can you fix it for me?

If one bug is the whole problem, the App Fix Ticket covers it: a written diagnosis first, then the fix. If the app has more going on, the Launch Check is a written check of the whole app. Each issue found is rated critical, high, medium or low, with where it is and how it was confirmed. No issues worth fixing, no charge. If the Launch Check finds nothing rated medium or higher, Simon refunds the full $450. You keep the report either way.

A different error?

Describe it and get a written quote

Send the error and what you were doing. You get a written scope, a fixed price and a timeline; we reply by email, usually within one business day.