Permission and RLS issues
Permission issues often appear as permission denied, HTTP 401/403, empty query results, or failed writes. Diagnose both role grants and RLS policies.
Identify the issue type
| Symptom | Check first |
|---|---|
permission denied for table | Table and schema permissions |
permission denied for sequence | Identity sequence permissions |
| Query succeeds but returns an empty array | RLS using condition mismatch |
| Insert or update fails | RLS with check condition mismatch |
| Unauthenticated access fails | anon role or sign-in state |
Check table permissions
select grantee, privilege_type
from information_schema.role_table_grants
where table_schema = 'public'
and table_name = 'todos'
order by grantee, privilege_type;
Grant permissions as needed:
grant usage on schema public to authenticated;
grant select, insert, update on public.todos to authenticated;
grant usage, select on all sequences in schema public to authenticated;
Check RLS status
select schemaname, tablename, rowsecurity
from pg_tables
where schemaname = 'public'
and tablename = 'todos';
View policies:
select policyname, cmd, roles, qual, with_check
from pg_policies
where schemaname = 'public'
and tablename = 'todos';
Empty result diagnostics
If a query returns an empty array without an error, RLS usually filtered all rows. Check:
- Whether the current user is signed in.
- Whether the ownership field equals
auth.uid(). - Whether the policy uses the correct role, such as
authenticated. - Whether the query goes through a view and whether that view bypasses RLS.
select auth.uid();
When auth.uid() is null, user-based policies deny access.
Fix recommendations
- Design RLS before opening table permissions to frontend access.
- Use
WITH CHECKforinsertandupdateto prevent writing data for other users. - Index RLS filter fields to avoid slow permission checks.
- Put admin operations behind Cloud Functions or CloudBase Run instead of relaxing frontend RLS.
For policy examples, see Basic permissions.