DEV Community

Cover image for Supabase error 42501 "permission denied for table": the safe fix before Oct 30
PatchAndShip
PatchAndShip

Posted on Originally published at patchandship.com

Supabase error 42501 "permission denied for table": the safe fix before Oct 30

If a request to your Supabase table suddenly fails with 42501 "permission denied for table", the role making the request has no grant on that table. It's a one-line error with a tempting one-line fix, and that fix is exactly what you want to avoid.

TL;DR: Grant only what each role needs, keep RLS on with policies, and never GRANT ALL to anon.

Status as of Oct 26, 2026: the change below hasn't reached existing projects yet. From Oct 30, Supabase is scheduled to apply it to all existing projects.

What Supabase will change

Here's how Supabase's changelog puts it: once the change reaches a project, "new tables you create in public schema require an explicit opt-in (via a Postgres grant) before the Data API can see them." The Data API is the auto-generated REST and GraphQL layer, the thing supabase-js talks to.

The timeline:

  • April 28, 2026: new projects could opt in at creation.
  • May 30, 2026: became the default for all new projects, rolled out gradually over a few weeks.
  • October 30, 2026: from Oct 30, Supabase is scheduled to apply it to all existing projects.

What stays the same, per Supabase:

  • Existing tables keep their current grants and stay reachable.
  • RLS behavior is unchanged.
  • Apps that only use a direct Postgres connection aren't affected.
  • Only the public schema is affected. storage, auth, realtime and custom schemas aren't.

Source: Supabase changelog: Tables not exposed to Data and GraphQL API automatically (published Apr 28, 2026).

What the error looks like

"code": "42501",
"message": "permission denied for table your_table"
Enter fullscreen mode Exit fullscreen mode

The response also includes a hint with a suggested GRANT statement. Read it before you paste it. The right grant depends on who should see the data.

Same code, different cause: if the message says "new row violates row-level security policy", your grant is fine. An RLS policy's with check rejected the row. Fix the policy, leave the grants alone, and don't turn RLS off. (Supabase: Database API 42501 errors)

Why GRANT ALL to anon is the wrong fix

anon is the role behind your app's public key, the one that ships in your JavaScript. Your AI tool may suggest grant all on table ... to anon. The error disappears. Here's what you gave up to make that happen.

Supabase spells out the order of checks: "If the grant is missing, Postgres rejects the query before RLS comes into play." (Supabase changelog) Grants decide whether a role can reach a table. RLS decides which rows it can see (Supabase: Securing your API). Once the grant is in place, RLS is the only thing between a visitor and your data.

And if RLS is off on that table, Supabase's Security Advisor (lint 0013) doesn't sugarcoat it: "Anyone with your project URL can read, edit, and delete all data in this table because Row-Level Security is not enabled." (Supabase: Database Advisors)

Supabase's advice: "Use both controls for every exposed object."

The safe fix: grant, RLS and policies in one migration

Supabase's recommended fix treats three things as one unit: the grant, enabling RLS, and the policies you need. Ship them in one migration so none of them can go out without the others.

Here's an example for an orders table where each row belongs to one signed-in user via user_id:

begin;
alter table public.orders enable row level security;
revoke all on table public.orders from anon, authenticated;
grant select, insert, update, delete on table public.orders to authenticated;

create policy "orders_select_own" on public.orders
  for select to authenticated
  using ((select auth.uid()) = user_id);

create policy "orders_insert_own" on public.orders
  for insert to authenticated
  with check ((select auth.uid()) = user_id);

create policy "orders_update_own" on public.orders
  for update to authenticated
  using ((select auth.uid()) = user_id)
  with check ((select auth.uid()) = user_id);

create policy "orders_delete_own" on public.orders
  for delete to authenticated
  using ((select auth.uid()) = user_id);
commit;
Enter fullscreen mode Exit fullscreen mode

Supabase's changelog example also grants service_role. Add that if your own server code reads or writes the table:

grant select, insert, update, delete on table public.orders to service_role;
Enter fullscreen mode Exit fullscreen mode

Only grant anon access to data that signed-out visitors really need, and usually only select.

Test it: sign out and read the table with just the public key. You should get an error or an empty list. Only test projects you own or are authorized to test.

New projects hit this too

New projects have behaved this way by default since the May 30, 2026 rollout, so a brand-new project may need the same grants today.

If an AI tool writes your migrations

Give it the rule before it writes the migration. Paste this into your project instructions:

When you add a new table to Supabase, write one migration that does three things together: grant only the access each role needs (never GRANT ALL to anon), enable Row Level Security on the table, and add one policy per operation that limits each signed-in user to their own rows. Do not turn Row Level Security off to make an error go away.
Enter fullscreen mode Exit fullscreen mode

Then check what it writes against the example above before you run it.

Quick FAQ

Will my existing tables break? No. Supabase says existing tables keep their grants and stay reachable, and RLS behavior is unchanged. The change only covers new tables in public.

Does it affect storage or auth tables? No. Only default privileges in public.

Where do I see affected tables? In the Security Advisor. Supabase: "From today through October 30, the Security Advisor flags affected tables per project and shows the remediation SQL." Supabase also planned email notices to owners and admins of active projects on September 23 and October 23, 2026.

Sources

All checked Oct 26, 2026. The rule "grant only what each role needs…" is our own guidance.


The full guide, kept up to date after Oct 30, is on our site. Want a second pair of eyes on your app? Try the free instant check (results in your browser in a few seconds, and it doesn't store your results) or ask for the free emailed mini check (one page within 24 hours). For app owners or people they authorize.

PatchAndShip Team

Top comments (0)