Supabase RLS: empty results or 'new row violates row-level security policy'
With RLS enabled and no matching policy, Postgres denies by default: reads return an empty array and writes fail with error 42501. Add a policy for each operation (select, insert, update, delete) for the right role, and make sure the request carries the signed-in user's session.
Cause: RLS is on but no policy allows this role and operation, or the row fails the policy's with check, or an insert chained with .select() has no SELECT policy.
How to fix it
- Check which policies exist for the table and role (
authenticatedvsanon). - Add one policy per operation; inserts use
with check, updates needusing+with checkand a SELECT policy. - Ensure the client is signed in so
auth.uid()is set, and the row'suser_idmatches. - If you chain
.select()after an insert to get the row back, you also need a SELECT policy. - Never fix this by using the service_role key in the browser.
Example
Illustrative example, not from a customer project.
+ create policy "own rows: select" on public.todos
+ for select to authenticated using ((select auth.uid()) = user_id);
+ create policy "own rows: insert" on public.todos
+ for insert to authenticated with check ((select auth.uid()) = user_id);
FAQ
Why do I get [] instead of an error?
A using clause that filters out every row returns zero rows silently. Only grants and with check violations raise 42501.
Should I just disable RLS?
No. Tables exposed through the API without RLS can be read by anyone holding your public anon key.
Get a $149 diagnosis See pricing Still exposed or still logged out? Get an auth & database security fix (Supabase, Firebase).
- Fixed, or we keep working free until it is (within the written scope).
- A human reviews every fix before it ships.
- read-only access; fixes arrive as pull requests you merge
- Your $149 diagnosis is credited in full toward any paid job.