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
- No policy exists for this operation (e.g. SELECT exists, INSERT does not).
- The INSERT/UPDATE policy lacks a WITH CHECK clause that the new row satisfies.
- The owner column is null or filled from the wrong source on insert.
- The request is made without the user session (anon key only).
- The role is missing table privileges.
How to diagnose it
- Note the exact operation and table from the error.
- List the table’s policies for that operation in the Supabase dashboard.
- Confirm the client sends the user session on that request.
- 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 →