Rowpass 42501: permission denied for table Not affiliated with Supabase

Rowpass / Guides

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.

StepWho creates the tableGrants the API roles receivesupabase-js as a signed-in user
Old project, table made in the SQL EditorpostgresSELECT, INSERT, UPDATE, DELETE (automatic)Works
Old project, table made by prisma migrateprismaNone42501
Any project after the 2026 change, either waypostgres or prismaNone42501

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.

    The SQL appears here as soon as a table is found.

    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

    Related

    Did it fix your error?