Supabase Returns Empty Data With No Error? It's Usually Row Level Security

Quick answer: Supabase query returns [] with no error, or insert fails with "new row violates row-level security policy"? Why RLS does that, and the SQL policies to fix it.

The error

Your table clearly has rows in the Supabase dashboard, but your app gets nothing back:

{ data: [], error: null }

No red errors in the console, just a blank list or dashboard. The write-side version of the same problem does throw an error:

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

(Postgres error code 42501.)

Why it happens

When Row Level Security (RLS) is enabled on a table, Postgres denies everything that no policy explicitly allows. It handles reads and writes differently:

The Supabase Table Editor shows all rows because it doesn't run as your app's user, which is why the data "is there" but your app can't see it.

Common triggers in AI-built apps:

Fixes

1. Confirm it's RLS

2. Add a SELECT policy that matches who should see the rows

Private rows owned by each user:

create policy "Users read own projects" on public.projects
  for select to authenticated
  using ((select auth.uid()) = user_id);

Public data anyone can read (for example, a product list):

create policy "Anyone can read products" on public.products
  for select to anon, authenticated
  using (true);

3. Fix "new row violates row-level security policy" on insert

Add an INSERT policy, and make sure user_id is set to the signed-in user:

create policy "Users insert own projects" on public.projects
  for insert to authenticated
  with check ((select auth.uid()) = user_id);

alter table public.projects alter column user_id set default auth.uid();

For the full set of select, insert, update and delete policies, see our Supabase RLS errors guide.

4. Make sure the user is signed in before you query

const { data: { user } } = await supabase.auth.getUser();
if (!user) {
  // show the login screen instead of querying
}
const { data, error } = await supabase.from('projects').select('*');

If the page loads data before the session is restored, the first query runs as anon and returns []. Wait for the session (or listen with onAuthStateChange) before fetching.

5. Rule out the non-RLS causes

6. Don't "fix" it by disabling RLS or using the service role key in the browser

Turning RLS off, or putting the service_role / sb_secret_... key in frontend code, makes the data readable (and often writable) by anyone who has your public anon key, which is visible to every visitor. Keep RLS on and write the policy you actually need.

Prompt to paste into Lovable, Bolt or Cursor

My Supabase query on the "projects" table returns an empty array with no error, but the table has rows. RLS is enabled. Show me the existing policies, then write the missing SELECT policy so authenticated users can read rows where user_id = auth.uid(). Make sure inserts set user_id to auth.uid(). Don't disable RLS and don't use the service_role key in client code.

FAQ

Why doesn't Supabase just show a permission error on select? That's how Postgres RLS works: it filters rows rather than rejecting the query. An empty result is the policy saying "you can't see any of these."

My update "succeeds" but nothing changes. Same problem? Yes. With no matching UPDATE policy (or a USING clause that doesn't match), the update affects zero rows without an error. Add .select() after the update to see what actually changed.

Is the anon key safe if RLS is on? The anon or publishable key is meant to be public. It's only safe because RLS policies limit what it can do, so every table the browser touches needs RLS and the right policies.

Related guides

Still stuck? Paste your exact error into the free Error Fixer to get a plain-English cause, the fix, and a prompt to paste back into your AI tool.

Get the free "10 Prompts That Fix Broken AI Code" pack

Join the UnbreakKit newsletter: one short email a week with real errors from Cursor, Lovable, Bolt & Replit users and how to fix them. Unsubscribe anytime.

Newsletter signup powered by beehiiv. We only use your email to send the newsletter.