← All Articles 🔐 Auth & Identity High By Nishant Kumar Verma

Supabase 42501 "Permission Denied" and the End of anon Keys

New Supabase tables can fail with 42501 permission denied until you grant access, and anon and service_role keys end in 2026. Safe fixes for both.

SEC-006 SEC-014 PRJ-001

Quick Answer

Two Supabase changes land on apps built with Lovable, Bolt and other AI tools in late 2026. New tables in the public schema no longer get Data API access by default (scheduled for all existing projects on 30 October 2026), so a new table can fail with error 42501, "permission denied for table", until you grant access and add Row Level Security. And Supabase is deprecating the legacy anon and service_role keys by the end of 2026, so move to sb_publishable_ and sb_secret_ keys now.

Change 1: New Tables Are Not Exposed Automatically

Until this change, every new table in public was granted select, insert, update and delete for the anon, authenticated and service_role roles. Supabase's breaking-change notice, published 28 April 2026, ends that default:

Date What happens
28 April 2026Opt-out checkbox when you create a project
30 May 2026The new behaviour becomes the default for new projects
30 October 2026Scheduled to apply to all existing projects

Two details matter. Tables you already have keep their grants, so nothing that works today stops working. And only the Data API is affected: supabase-js, PostgREST and GraphQL. Direct Postgres connections are not.

What changes is the next table your AI tool creates. The app calls it through supabase-js and gets this back:

{
  "code": "42501",
  "message": "permission denied for table your_table",
  "hint": "Grant the required privileges to the current role with: GRANT SELECT ON public.your_table TO anon;"
}

Fixing 42501 Without Opening the Table to Everyone

The hint is the trap. Paste it, or ask your AI tool to "fix the permission error", and the error goes away. But if the table has no Row Level Security, you have just made it readable by anyone with your publishable key, and that key ships in your front end. That is the setup behind the Lovable data leak study, where 24,664 sites sent database records to visitors who never logged in.

-- Bad: makes the error disappear and opens the table to every visitor
grant select, insert, update, delete on public.orders to anon;

Supabase's own fix pairs the grant with RLS. Grant only the roles that need the table, turn RLS on, then write policies:

-- Fixed: signed-in users only, and each user sees only their own rows
grant select, insert, update, delete on public.orders to authenticated;
grant select, insert, update, delete on public.orders to service_role;

alter table public.orders enable row level security;

create policy "Users read their own orders"
on public.orders for select
to authenticated
using ( (select auth.uid()) = user_id );

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

Add update and delete policies the same way if users change or remove rows. With RLS on, an action that has no policy is refused, and Supabase's troubleshooting page lists that as another source of 42501.

Grant select to anon only for tables that really are public, such as a product catalogue, and still keep RLS on. If the 42501 comes from somewhere else (a custom schema, the auth schema, or an RLS rule blocking an insert), Supabase's 42501 troubleshooting page lists each cause.

Change 2: The anon and service_role Keys Are Being Retired

Supabase's API keys docs say it is deprecating the anon and service_role keys by the end of 2026. The replacements:

Legacy key Replacement Where it may live
anon sb_publishable_... Browsers, mobile and desktop apps. Safe to expose, because it only reaches what RLS allows.
service_role sb_secret_... Servers you control only. It bypasses every RLS policy.

The new secret keys come with a guard rail the old key never had: the docs say a secret key does not work in a browser, and Supabase returns HTTP 401 when the User-Agent shows one. In Supabase's original announcement, the late-2026 step (marked "TBC") is that legacy keys are deleted, and it warns that apps which have not migrated will break.

Migrate Your Keys Step by Step

Supabase's migration guide breaks it into six steps:

  1. Create the new keys in Settings, then API Keys, on the "Publishable and secret API keys" tab. Creating them does not turn off the old ones.
  2. Front end: replace the anon key with the publishable key wherever your app ships it, usually an env var your build reads.
  3. Back end: replace service_role with a secret key on servers you control.
  4. Edge Functions: read the new keys from SUPABASE_SECRET_KEYS or SUPABASE_PUBLISHABLE_KEYS, which hold JSON keyed by name, for example JSON.parse(Deno.env.get('SUPABASE_SECRET_KEYS')!)['default']. A function called only with an API key needs verify_jwt = false and its own check in code.
  5. Find the callers you forgot: old app versions, CI, cron jobs, workers, third-party integrations and pg_net calls. The guide says there is no automatic usage indicator, so list them yourself.
  6. Deactivate the legacy keys in Settings, then API Keys. The guide says you can re-activate them if you missed a client.
# Example only: your variable names may differ
# Front end (.env read by the build)
VITE_SUPABASE_PUBLISHABLE_KEY=sb_publishable_...

# Server only: never behind a VITE_ or NEXT_PUBLIC_ prefix
SUPABASE_SECRET_KEY=sb_secret_...

If a service_role Key Ever Leaked

Migration is also the moment to check whether the old key ever left the server. Apiiro's November 2026 research found 122 Supabase service-role keys in public Claude Code settings files alone (see the "don't ask again" leak). Supabase's rotation guide says to fix the cause of the leak first. Then create a new secret key, move every component to it, and retire the leaked one. For a leaked legacy service_role key, also replace anon with a publishable key and revoke the legacy JWT secret that signed it. The guide also warns that re-enabling the legacy keys brings the compromised key back.

VibeDoctor's Vibe Check scans your repository for committed secrets and rates a committed .env holding a Supabase service_role key as critical, with the file path. It does not log in to your database, so check RLS with Supabase's Security Advisor. Free to sign up.

For writing the policies themselves, see Supabase Row Level Security in vibe-coded apps.

FAQ

Why does my new Supabase table say "permission denied for table"?

Under Supabase's new default grants, new tables in public are not exposed to the Data API until you grant access. Grant only the roles that need the table, enable RLS and add policies. Do not just grant everything to anon.

Will my existing tables stop working?

No. Supabase's changelog says existing tables keep their current grants. Only tables created after the change are affected.

When do the anon and service_role keys stop working?

Supabase's docs say they are deprecated by the end of 2026 and give no exact day. Its original announcement marks the late-2026 deletion step "TBC". Migrate now rather than waiting for a date.

Can I put the new secret key in my front end?

No. It bypasses RLS, and Supabase rejects it with HTTP 401 when it detects a browser. The publishable key is the one meant for front-end code.

Is the publishable key a secret?

No. Supabase says it is safe to expose, because it only reaches what RLS allows. That is why RLS on every exposed table is the part that keeps your data private.

Diagnose your codebase - free

VibeDoctor checks for SEC-006, SEC-014, PRJ-001 and 148 other issues across 21 diagnostic areas - security, performance, code quality, and more.

SCAN MY APP →