Supabase Row-Level Security Silently Returning Empty

RLS returning empty instead of an error is the most common Supabase debugging pit. How to spot it, how to debug it, how to design around it.

Problems/Supabase Row-Level Security Silently Returning Empty

Pain Point

Supabase Row-Level Security Silently Returning Empty

Supabase RLS returns empty instead of an error. How to spot the silent fail, how to debug it, and how to design your code so it never surprises you.

The bug that wastes a full day

You write a query. Locally it returns three rows. In production it returns an empty array. No error. No log. Just empty.

This is RLS doing exactly what it should: filtering out rows the user is not allowed to see. The problem is that it does so silently, and your client code happily renders an empty state as if the data were truly missing.

I burned half a day on this the first time. It is worth learning the shape before it hits you.

How to reproduce it on purpose

  • Write a select from a table with RLS on.
  • Run it with a user whose auth.uid() does not match any row policy.
  • You get { data: [], error: null }.

Not an auth error. Not a permission error. Just empty.

How to debug it

Three things, in order:

  1. Log auth.uid() in the query. supabase.rpc('debug_uid') that returns auth.uid(). If it is null, your session is not being forwarded.
  2. Read the policies directly. SELECT * FROM pg_policies WHERE tablename = ''your_table'';. Check the USING clause matches what you expect.
  3. Run the query as that user in SQL: SET LOCAL role = authenticated; SET LOCAL request.jwt.claim.sub = ''<user_id>''; then run the query. If it returns empty here, RLS is the cause.

The design move that prevents it

Always read auth.uid() first in your code path. If it is null before you even hit the database, show an auth error, not an empty state. If it is set but the query returns empty, log it with the user id so you can tell silent-RLS from real-empty.

On the server side: wrap cross-user queries in a service-role client so RLS does not apply, and enforce authorization yourself explicitly. The service-role client belongs on the backend, never in the mobile bundle.

The boilerplate version

AI App Factory ships a Supabase wrapper that logs auth.uid() on every query in development, and flags empty results so you see them in the console. It also separates the anon-key client (mobile) from the service-role client (backend) by construction.

See pricing

Skip this problem entirely.

AI App Factory handles the boring infrastructure so you can build the product.

AI App FactoryProblemsSupabase Row-Level Security Silently Returning Empty