Fix guides

Supabase "permission denied for table" (42501) on a new table

From October 30, 2026, new Supabase tables are not reachable from supabase-js until you GRANT access. Why your new Lovable, Bolt, or Cursor feature returns 42501, and the safe way to fix it without exposing every table.

What you are seeing

Why it happens

  1. Supabase changed a platform default. Per its changelog, new tables in the public schema no longer get automatic grants for the anon, authenticated, and service_role roles. New projects got this from May 30, 2026, and it is applied to all existing projects on October 30, 2026.
  2. Your AI tool or migration created the table with create table and policies but no grant, because that used to be enough.
  3. Grants and RLS are separate layers. The grant decides whether a role can touch the table at all, and RLS decides which rows it sees. A missing grant fails before any policy runs, so editing policies will not help.
  4. Existing tables keep the grants they already have, which is why only new features break.
  5. If you only reach Postgres through a direct connection (an ORM or server with a connection string), this change does not affect you.

How to fix it

1. Confirm it is the grant, not RLS

The error code 42501 with "permission denied for table" means a missing grant. An RLS problem looks different: "new row violates row-level security policy" on writes, or an empty array on reads (see the RLS guide).

2. List tables in public that the API roles cannot reach

select c.relname as table_name,
       c.relrowsecurity as rls_on,
       has_table_privilege('authenticated', c.oid, 'select') as auth_can_select,
       has_table_privilege('anon', c.oid, 'select') as anon_can_select
from pg_class c
join pg_namespace n on n.oid = c.relnamespace
where n.nspname = 'public' and c.relkind = 'r'
order by 1;

3. Grant only what each table needs, and only to the right role

-- signed-in users only (most app tables)
grant select, insert, update, delete on public.your_table to authenticated;

-- add anon only if logged-out visitors must read it
grant select on public.your_table to anon;

-- server code using the service role key
grant select, insert, update, delete on public.your_table to service_role;

4. Turn on RLS and add policies in the same migration

alter table public.your_table enable row level security;

create policy "owner can read" on public.your_table
  for select to authenticated using (auth.uid() = user_id);

create policy "owner can write" on public.your_table
  for insert to authenticated with check (auth.uid() = user_id);

5. Avoid the quick fix that leaks

Do not paste grant ... on all tables in schema public to anon, and do not restore the old default privileges, just to make the error go away. That re-exposes every table, including any with RLS off, to anyone holding your public anon key. Run the query from step 2 first and make sure every table you grant has rls_on = true.

6. Tell your AI tool about the new rule

Add this to your project rules (.cursorrules, Lovable knowledge, or Bolt prompt): "Every new table in public needs explicit GRANT statements to authenticated (and anon only if public), plus ENABLE ROW LEVEL SECURITY and policies, in the same migration."

How to know it is fixed

Re-run the query from step 2: every table your app uses shows true for the roles it needs and true for rls_on. Then use the feature while signed in and while signed out, and confirm signed-in users see only their own rows and logged-out visitors get nothing they should not.