Prisma tables return "permission denied for table"
Prisma made the table, so the API roles were never granted. Here is the proof and the fix.
Worked example
Supabase's Prisma guide has you create a database user named prisma and run migrations as that user. Supabase's automatic grants were default privileges set for role postgres. The PostgreSQL manual says what that means: at object creation time, new object permissions are only affected by the default privileges of the current role
.
| Step | Who creates the table | Grants the API roles receive | supabase-js as a signed-in user |
|---|---|---|---|
| Old project, table made in the SQL Editor | postgres | SELECT, INSERT, UPDATE, DELETE (automatic) | Works |
Old project, table made by prisma migrate | prisma | None | 42501 |
| Any project after the 2026 change, either way | postgres or prisma | None | 42501 |
We ran rows one and two in our test: with the old defaults in place, a table created by postgres was readable by anon, and a table created a moment later by a prisma role answered permission denied for table for anon and for service_role.
Prisma itself is not affected. It connects straight to Postgres as its own user. The error appears only when the same tables are read through the Data API, for example with supabase-js in the browser.
See who made each table
select
c.relname as table_name,
c.relowner::regrole as created_by,
has_table_privilege('anon', c.oid, 'select') as anon_can_select,
has_table_privilege('authenticated', c.oid, 'select') as authenticated_can_select,
has_table_privilege('service_role', c.oid, 'select') as service_role_can_select
from pg_class c
where c.relnamespace = 'public'::regnamespace
and c.relkind in ('r', 'p')
order by 1;
The fix: grants in a migration
Add the grants as SQL in a Prisma migration, so every environment gets them. For a table where each user owns their rows:
-- public.your_table: owner only
alter table public.your_table enable row level security;
revoke all on table public.your_table from anon, authenticated;
grant select, insert, update, delete on table public.your_table to authenticated;
grant select, insert, update, delete on table public.your_table to service_role;
drop policy if exists your_table_select_own on public.your_table;
create policy your_table_select_own on public.your_table
for select to authenticated
using ( (select auth.uid()) = user_id );
drop policy if exists your_table_insert_own on public.your_table;
create policy your_table_insert_own on public.your_table
for insert to authenticated
with check ( (select auth.uid()) = user_id );
drop policy if exists your_table_update_own on public.your_table;
create policy your_table_update_own on public.your_table
for update to authenticated
using ( (select auth.uid()) = user_id )
with check ( (select auth.uid()) = user_id );
drop policy if exists your_table_delete_own on public.your_table;
create policy your_table_delete_own on public.your_table
for delete to authenticated
using ( (select auth.uid()) = user_id );
The line that enables row level security matters as much as the grants: a granted table with RLS off is readable by anyone holding the publishable key. Paste your create table SQL from the migration into the tool below to get this for every table at once.
Supabase's Prisma guide also notes: if you use Prisma only, you can turn the Data API off in the API settings. Then none of this applies.
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
Why did it work before and break now?
If your migrations ran as the postgres user, tables used to get automatic grants and no longer do after the 2026 change. If they ran as a separate prisma user, the tables never had API grants and something else was granting them.
Does Drizzle behave the same way?
The rule is about which database role creates the table, not which tool. We have only tested the role behaviour, not Drizzle itself.
Will prisma migrate reset remove the grants?
A reset replays your migrations. Grants that live in a migration come back. Grants you ran by hand in the SQL Editor do not.
Sources
- Supabase docs: Prisma, read 2026-11-10
- PostgreSQL manual: ALTER DEFAULT PRIVILEGES, read 2026-11-10
- Supabase changelog: Breaking Change: Tables not exposed to Data and GraphQL API automatically (published 2026-04-28), read 2026-11-10
- Our own test: the behaviour the reference pages describe, run against PGlite, read 2026-11-10
Related
- Local tables missing grants after db resetA reset replays your migrations. Without grants in them, tables come back closed.
- Permission denied for schema publicThe role cannot enter the schema, so table grants never get looked at.
- Free migration template and grants auditTwo .sql files. One creates a table the right way. One shows what every table grants today.
- Is my Supabase project affected?Run one query. No rows means new tables get no grants.