Permission denied for view
A view has its own grants. With security_invoker, its tables need them too.
Four cases
| View option | Grant on the view | Grant on the table under it | Result for the caller |
|---|---|---|---|
| Default | No | Any | 42501, permission denied for view v |
| Default | Yes | No | Rows returned, read with the view owner's rights |
security_invoker = on | Yes | No | 42501, permission denied for table t |
security_invoker = on | Yes | Yes | Rows returned, filtered by the caller's policies |
We ran all four in our test. Row two is the dangerous one: the view hands out rows the caller has no right to read on the table itself. The PostgreSQL manual states the rule for rows three and four: the user of a security invoker view must have the relevant permissions on the view and its underlying base relations
.
See your views
select
c.relname as view_name,
coalesce(c.reloptions::text, '') like '%security_invoker=on%'
or coalesce(c.reloptions::text, '') like '%security_invoker=true%' as security_invoker,
has_table_privilege('anon', c.oid, 'select') as anon,
has_table_privilege('authenticated', c.oid, 'select') as authenticated
from pg_class c
where c.relnamespace = 'public'::regnamespace
and c.relkind = 'v'
order by 1;
The fix that keeps row level security working
alter view public.your_view set (security_invoker = on);
grant select on public.your_view to authenticated;
grant select on public.your_table to authenticated;
Then make sure the table under the view has row level security and policies. Paste the table into the tool below to get them. The tool reads tables, not views: for a view it shows a note instead of SQL.
Supabase's lint 0010 flags views that are not security invoker; its documentation says to set with (security_invoker=on) when a view should respect row level security policies.
Write the fix for your tables
One table is free. The whole schema with a rollback file is $9 one-time.
Waiting for a paste.
Who can reach each table
Your tables show up here, one row each, with a choice of who can reach them.
Parsed in your browser. Your SQL is never uploaded, and no site or database is scanned.
Questions
Do I need both grants?
With security_invoker on, yes: one on the view and one on each table it reads. Without it, only the view needs a grant, and the view then reads with its owner's rights.
Does the 2026 change apply to views?
Supabase describes grants as deciding which roles reach a table, view or function over the Data API. We have not seen an official line that says how new views are treated after 30 October 2026, so check the grant on your view with the query above.
Sources
- PostgreSQL manual: CREATE VIEW, read 2026-11-10
- Supabase docs: Securing your API, read 2026-11-10
- splinter (Supabase Postgres linter): lint documentation, main branch, read 2026-11-10
- Our own test: the behaviour the reference pages describe, run against PGlite, read 2026-11-10
Related
- Every cause of 42501 on Supabase42501 means "insufficient privilege". Match your message to a row.
- Grants vs row level securityTwo checks run on every request. A grant comes first.
- All 30 Supabase advisor lintsName, level and one-line summary for every lint, 0001 to 0030.
- Check the grants on a tableOne query lists every role and privilege on a table.