BUILD OS · AUTHORIZATION

Supabase returns “permission denied” or an empty list after enabling RLS

Short answer

Row level security denies by default: with RLS on and no policy allowing that operation for that role, Postgres refuses the query (or returns zero rows for SELECT). Write one owner-scoped policy per operation, add WITH CHECK for inserts and updates, and make sure the request carries the user’s session. Never fix it by disabling RLS.

Symptoms

  • “new row violates row-level security policy for table …” on insert.
  • “permission denied for table …” (42501).
  • A select that used to return data now returns an empty array.

Why this happens

Once RLS is enabled, every row access must be allowed by a policy that matches the operation and the requesting role. auth.uid() is null when the request has no session, so owner policies match nothing.

Most likely causes

  1. No policy exists for this operation (e.g. SELECT exists, INSERT does not).
  2. The INSERT/UPDATE policy lacks a WITH CHECK clause that the new row satisfies.
  3. The owner column is null or filled from the wrong source on insert.
  4. The request is made without the user session (anon key only).
  5. The role is missing table privileges.

How to diagnose it

  1. Note the exact operation and table from the error.
  2. List the table’s policies for that operation in the Supabase dashboard.
  3. Confirm the client sends the user session on that request.
  4. For inserts, check that the owner column receives the signed-in user id.

Fix

  • Create least-privilege policies per operation, scoped to ownership or tenant membership.
  • Add WITH CHECK to insert and update policies.
  • Set the owner column server-side or with a database default, never from a free-form client field.
  • Keep the service-role key server-only, and authorize before using it.

Verify the fix

Do not close the issue until these checks pass in the environment that matters:

  • The owner can read and write their rows.
  • A second account gets nothing back for the first account’s rows.
  • A request without a session is refused.

Related Ship Passport checks

Build School’s production-readiness standard marks this area as proven only with evidence from these checks (it is Build School’s own standard, not an external certification):

  • criticalRLS is enabled and tested on every exposed table
  • criticalUser A cannot access user B’s data

Common false fix

Disabling RLS or adding a policy that allows every authenticated user makes the error disappear by exposing every row.

Documented evidence

Documenté

Une session répond à « qui es-tu ? » ; l’autorisation répond à « peux-tu accéder à cette ressource ? ».

Source note: authentification-nest-pas-autorisation · Updated 2026-09-26. This is documentation provenance; it is not a claim about frequency.

Related build: atamatax

Primary references

Related pattern

Authentication is not authorization →

Build OS stage

Authorization · Explore Build OS →

Run ShipCheck →Learn the systemGet help fixing it