shipfix

Common errors · Supabase (Lovable, Bolt, any client)

Supabase: new row violates row-level security policy

Cause RLS is on and no INSERT policy accepts the row your app sends (often user_id is not auth.uid(), or the session is not ready yet).
Fix Add an insert policy tied to auth.uid(), let the database fill user_id, and add UPDATE/SELECT policies if you upsert or select after insert.

new row violates row-level security policy for table "profiles"

Have it fixed for you, 290 EURSee the fix step by step

What you receive a diff on a branch of your repository, and a correction note: root cause, change made, how to verify it. See a sample report

The insert works in the SQL editor and fails from the app. That is expected: the SQL editor runs as the postgres role and skips RLS, your app runs as anon or authenticated and does not.

Where it breaks

Your approle: authenticatedINSERT profilesuser_id = 'abc'WITH CHECKuser_id = auth.uid()Rejecteduid null or differentsendsrowpolicyrunsfalseYour approle: authenticatedINSERT profilesuser_id = 'abc'WITH CHECKuser_id = auth.uid()Rejecteduid null or differentsends rowpolicy runsfalse
The SQL editor runs as postgres and skips RLS. Your app runs as anon or authenticated, so the INSERT policy decides.

Which case is yours?

  1. RLS is enabled on the table and there is no INSERT policy whose WITH CHECK clause is true for the row you send.
  2. Most often the row carries a user_id that does not equal auth.uid(), or the request is sent before the session exists, so auth.uid() is null.
  3. An upsert needs both an INSERT and an UPDATE policy. A select after insert (.insert().select()) also needs a SELECT policy.

The fix, step by step

  1. Check who the request runs as. In the browser, log the session right before the insert: if it is null, the request is anonymous.
    const { data: { session } } = await supabase.auth.getSession();
    console.log(session?.user.id);
  2. Add an insert policy tied to the signed-in user, and let the database fill user_id so the client cannot send a wrong one.
    alter table profiles alter column user_id set default auth.uid();
    
    create policy "insert own row" on profiles
      for insert to authenticated
      with check (user_id = auth.uid());
  3. If you use upsert or .select() after the insert, add the matching policies.
    create policy "read own row" on profiles
      for select to authenticated using (user_id = auth.uid());
    create policy "update own row" on profiles
      for update to authenticated using (user_id = auth.uid()) with check (user_id = auth.uid());
  4. Never fix it by disabling RLS or by putting the service_role key in the frontend. Both make every row readable and writable by anyone who opens the browser console.

How to check it is fixed

Sign in as a test user, insert a row: it succeeds. Sign in as a second user and try to read the first user's row: you get an empty result, not an error.

Delivery example for this error

migration.sql · on a branch of your repository- insert ... user_id: undefined+ alter column user_id+ set default auth.uid();
Verification checklist in the note
  • Root cause: RLS is on and no INSERT policy accepts the row your app sends (often user_id is not auth.uid(), or the session is not ready yet).
  • Change made: Add an insert policy tied to auth.uid(), let the database fill user_id, and add UPDATE/SELECT policies if you upsert or select after insert.
  • Sign in as a test user, insert a row: it succeeds.
  • Sign in as a second user and try to read the first user's row: you get an empty result, not an error.

Illustrative: the real diff is written against your repository. Full sample report

Still stuck on your own code?

Send the repository and the exact error. Fix: 290 EUR all taxes included, delivered within 72 hours, refunded if not resolved.

Private repository: paste its link anyway, we reply by email within one business day with the GitHub account to invite with read access. A zip link (Google Drive, Dropbox) works too. Never send passwords or secret keys in this form.

Fix: One blocking auth, Supabase RLS or deployment problem fixed in your repository.290 EUR all taxes included · 72 hours

Secure payment by Stripe. Not resolved within 72 hours: refunded in full.

Not sure it is this error? Ask for a free written diagnosis, no card needed.