# select returns an empty array but the table has data

> Why supabase.from().select() returns [] with no error when rows exist, usually Row Level Security or a missing session, and how to test the query as the right role.

Source: https://supahost.app/kb/empty-data-array · Updated 2026-11-11

An empty array with no error almost always means Row Level Security filtered every row out. RLS doesn't raise an error on reads. It quietly returns only the rows the caller may see, and that can be none.

## What you see

```js
const { data, error } = await supabase.from('bookings').select('*')
console.log(data, error) // [] null
```

The Table Editor in Studio shows plenty of rows. Studio connects as the `postgres` role, which owns the tables you created, and owners aren't subject to RLS, so it sees everything. Your app doesn't.

## Why it happens

Common causes, roughly in order of frequency:

1. **RLS is on and there's no `select` policy** for the caller's role. With RLS enabled and no policies, nobody except owners and `service_role` sees anything.
2. **The request runs as `anon`, not `authenticated`.** The user isn't signed in, the session hasn't loaded yet, or a server-side client was created without the user's cookies. A policy written `to authenticated` doesn't apply to `anon`.
3. **The policy doesn't match the data.** For example `user_id` holds a different value than `auth.uid()`, or rows were imported with `user_id` empty.
4. **The filter matches nothing**: wrong case in `.eq('status', 'Confirmed')`, or comparing a timestamp to a date string in another time zone.
5. **You're talking to a different project** than the one you're looking at in Studio.

## How to fix it

1. Check RLS and the policies on the table:

   ```sql
   select relname, relrowsecurity from pg_class where relname = 'bookings';
   select policyname, cmd, roles, qual from pg_policies where tablename = 'bookings';
   ```

2. Run the query as the role your app uses. Replace the UUID with a real user id from `auth.users`:

   ```sql
   begin;
   set local role authenticated;
   select set_config('request.jwt.claims',
     '{"sub":"8f1c2c4e-0000-4000-8000-000000000001","role":"authenticated"}', true);
   select * from public.bookings limit 5;
   rollback;
   ```

   If this returns nothing, the problem is the policy, not your client code.

3. Add or fix the policy:

   ```sql
   create policy "Guests read their own bookings"
   on public.bookings for select
   to authenticated
   using ((select auth.uid()) = guest_id);
   ```

4. Confirm the client has a session before querying:

   ```js
   const { data: { user } } = await supabase.auth.getUser()
   if (!user) console.warn('Not signed in: this query runs as anon')
   ```

   On the server with `@supabase/ssr`, make sure `createServerClient` gets the request cookies through `getAll`/`setAll`.

5. `update()` and `delete()` can also return an empty array: the rows exist, but the caller has no `select` policy to read them back, or no `update`/`delete` policy that matches them.

## On SupaHost

- Check the URL. Every project has its own host, such as `https://app-one.acme.supahost.space`, and its own keys. Branches are separate projects with their own URL and keys. A branch created without "Copy data, users and files" starts with empty tables and no users.
- A request is limited to 1000 rows. If you get exactly 1000 rows and expected more, that's the cap, not RLS. Use `.range()` to page.

## Related

- [Row Level Security](/docs/database/row-level-security)
- [Row Level Security, simplified](/kb/rls-simplified)
- [service_role client still gets RLS errors](/kb/service-role-rls-errors)
- [Server-side auth and SSR](/docs/auth/server-side)
