Different permissions can produce the same observation
A request can succeed without returning a row. The table may be empty, the query filter may match nothing, or an RLS policy may hide every matching row. Those explanations have different security implications even though the client receives no data.
A rejected request is different again. Missing grants can produce a permission error. Record the evidence you actually have: returned rows, an empty successful read, a denied query, or a failed request. A status code alone is not a complete security verdict.
Five reproducible PostgreSQL cases
Download the fixture below and run it as an administrator in a disposable local database. It creates a separate visitor role and example schema inside a transaction. No real user accounts or records are needed, and ROLLBACK removes the fixture objects.
The two zero-row cases deliberately differ: filtered contains a row and enables RLS without granting a read policy; empty_table contains no row and leaves RLS disabled. Both grant SELECT to the visitor. Testing as the database owner would bypass the intended role check, so the script switches to the dedicated visitor.
Debug with known data and a known identity
On a local or staging project, create one synthetic record with a known owner. Check its existence using your administrative workflow, then request it as the intended user and as a different user. Confirm the project, schema, table and filters are the same in each request.
Inspect session state: a request made before sign-in is not the same as one carrying a valid user token. Inspect applicable grants and policies in your project. Keep the tokens and the returned record contents out of screenshots, analytics and public reports.
A good regression test includes a positive control: the correct owner can read the record. It also includes a negative control: another account cannot. A test where everybody receives an empty response may indicate that the app is broken rather than correctly isolated.
How RLS Checker reports an empty response
The public report marks an empty anonymous read as inconclusive. It does not turn it into a passed policy test. This is intentional: the external checker has no privileged inventory of your stored rows or your policy definitions.
The local fixture establishes PostgreSQL role and RLS behaviour. It does not reproduce every PostgREST status, client filter or Supabase configuration. Combine the external observation with database and application tests before concluding that private data is protected.
Reproduce the five local cases
Run only in a disposable local PostgreSQL database. The script creates synthetic data in a transaction, verifies permissions and rolls back its changes. It does not connect to a Supabase project.
Download the SQL fixture