Guide Supabase
Supabase error 42501 "permission denied for table": what it means and the safe fix
Short answer
Error 42501 "permission denied for table" means the role making the request has no grant on that table. From October 30, 2026, Supabase will stop exposing new tables in the public schema of existing projects to the Data API until you grant access. Grant only what each role needs, keep RLS on with policies, and never GRANT ALL to anon.
RLS means Row Level Security. Grants decide whether a role can reach a table at all; RLS decides which rows it can see (Supabase: Securing your API).
This change has not happened yet. Supabase plans to apply it to existing projects on October 30, 2026. As of it has not reached existing projects. We'll check this page again after that date.
What will Supabase change on October 30, 2026?
Supabase's changelog says that 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 Supabase's auto-generated REST and GraphQL layer (Supabase changelog).
Supabase's planned timeline:
- From April 28, 2026: new projects could opt in when they were created.
- From May 30, 2026: the new behavior became the default for all new projects, rolled out gradually over a few weeks.
- On October 30, 2026: Supabase plans to apply it to all existing projects.
What stays the same, according to Supabase:
- Existing tables keep their current grants and stay reachable.
- RLS behavior is unchanged.
- Apps that only use a direct Postgres connection are not affected.
- Only the
publicschema is affected. Tables instorage,auth,realtimeand custom schemas are unaffected.
What does error 42501 look like?
When a grant is missing, the Data API returns error 42501 with a message like this (Supabase changelog):
"code": "42501",
"message": "permission denied for table your_table"
The error also comes with a hint that suggests a GRANT statement. Read it before you copy it. The right grant depends on who should see the data, which is the next question.
Why is GRANT ALL to anon the wrong fix?
An AI tool may suggest a quick fix: grant all on the table to anon, the role behind your app's public key. The error goes away. Here's why that's risky.
Supabase explains 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. So once the grant exists, Row Level Security is the only thing left between a visitor and your data.
If RLS is off on that table, Supabase's own Security Advisor puts it plainly: 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, lint 0013)
Supabase's advice is to Use both controls for every exposed object.
(Supabase: Securing your API)
What is the safe fix?
Our rule at PatchAndShip: grant only what each role needs, keep RLS on with policies, and never GRANT ALL to anon.
Supabase's recommended fix treats three steps as one unit: the grant, turning on RLS, and the policies you need (Supabase changelog). Put all three in one migration, so one never ships without the others.
Here is an example for an orders table where each row belongs to one signed‑in user through a user_id column. Change the names to match your table.
revoke all on table public.orders from anon, authenticated;
grant select, insert, update, delete on table public.orders to authenticated;
alter table public.orders enable row level security;
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);
Supabase's example in the changelog also grants access to service_role. Add that line too if your own server code reads or writes this table:
grant select, insert, update, delete on table public.orders to service_role;
Grant anon access only for data that signed‑out visitors truly need, and usually only select.
Then test it: sign out and try to read the table with only the public key. You should get an error or an empty list.
Can new Supabase projects show the same error?
Yes. New projects have had this behavior by default since the May 30, 2026 rollout, so a brand new Supabase project may need the same grants today (Supabase changelog).
What should I tell Lovable or Bolt?
If you build with Lovable, Bolt or another AI tool, give it the rule before it writes the migration. You can paste this:
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.
Then check the migration it writes against the example above before you run it.
Can PatchAndShip check or fix this for me?
Yes. The Security Fix ($149) covers RLS fixes, storage policies, a bundle key scan, key rotation guidance and a short report. Security Fix: every critical issue on our checklist fixed within 48 hours of working access. Otherwise, a full refund if you ask within 14 days of purchase.
Not sure yet? PatchAndShip's free mini check is passive: it looks only at the public homepage, its scripts and its security headers (for exposed secret keys, missing headers and publicly listed storage links), and nothing else: no database queries, no logins, no changes. It returns a one‑page summary within 24 hours.
Prefer to do it yourself? Use the free Supabase RLS checklist.
What else do founders ask about October 30?
Do my existing tables break?
No. Supabase says existing tables keep their current grants and stay reachable through the Data API, and RLS behavior is unchanged. The change is about new tables you create in the public schema.
Does this affect storage or auth tables?
No. The change affects default privileges in the public schema only. Tables in storage, auth, realtime and custom schemas are unaffected, and apps that only use a direct Postgres connection are not affected.
Where do I see affected tables?
In the Supabase Security Advisor. Supabase says: "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.
Source for all three answers: Supabase changelog, “Breaking Change: Tables not exposed to Data and GraphQL API automatically”.
Where do these facts come from?
- Supabase changelog: Breaking Change: Tables not exposed to Data and GraphQL API automatically. Published 28 Apr 2026. Timeline, scope, error 42501, the recommended fix and the Security Advisor.
- Supabase Docs: Securing your API. Grants and RLS.
- Supabase Docs: Database Advisors. Lint 0013, RLS disabled on a public table.
All sources checked on . Our own guidance is labeled as ours.