Supabase "permission denied for table" (42501): how to fix it, plus a free scan

Error 42501 on a new Supabase table usually means a missing GRANT. From Oct 30, 2026, new public tables need one. Grant only what the app needs and keep RLS on.

Gary 路 Published 路 Updated 路 9 min read

Diagram: a new public table with no GRANT returns 42501 permission denied; with a least-privilege GRANT plus RLS and policies it works; existing tables keep their grants.
On this page
  1. What is Supabase changing on October 30, 2026?
  2. Will my existing tables break?
  3. Who is affected?
  4. What does the 42501 error look like?
  5. What it looks like when we reproduced it
  6. How do I fix permission denied for table in Supabase?
  7. A table only signed-in users use
  8. A table logged-out visitors should read
  9. What not to do
  10. Why grants and RLS are separate layers
  11. A prompt to paste into your AI builder
  12. How can I check my app before October 30?
  13. FAQ

Short answer: If your Supabase app shows "permission denied for table" (error 42501) on a table created after the change reached your project (October 30, 2026 for existing projects; earlier for most newer projects), the table is most likely missing a GRANT. Supabase's changelog says that from that date, new tables in the public schema "require an explicit opt-in (via a Postgres grant) before the Data API can see them." Add the specific grant your app needs, enable Row Level Security, and add policies.

By Gary, Greenlight My App 路 Updated Oct 29, 2026 路 Last checked against Supabase's changelog and docs: Oct 29, 2026 路 Drafted with AI tools and reviewed before publishing.

Want an outside look at your live app? Our Oct 30 check lists which tables your public key can reach and gives you a copy-paste prompt for your AI builder. It's read-only and educational, not a security audit. You can also run the free check from the home page.

What is Supabase changing on October 30, 2026?

Supabase is changing a default, in stages. In its own words: "On October 30, 2026 the setting will be applied it [sic] to all existing projects." Earlier stages already happened:

DateWhat Supabase says happens
April 28, 2026New projects can opt in to the new behavior at creation
May 30, 2026The new behavior becomes the default for new projects (gradual rollout)
October 30, 2026The new behavior is applied to all existing projects

Before this change, with default settings, Supabase granted select, insert, update and delete on every new public table to the anon, authenticated and service_role roles. Every table was reachable through the Data API the moment it was created. After the change, a new table exists in your database but your app can't reach it until you grant access.

This is a default your project controls. Supabase made new tables less reachable by default; the extra step is deciding who should have access.

Will my existing tables break?

No. Supabase says: "Existing tables are not affected in your project, they keep their current grants and stay reachable."

The change only bites when something creates a new table after the change reaches your project. That includes more than you might expect:

  • A migration you or your AI builder writes for a new feature
  • A brand-new Supabase project
  • A preview branch built from your migrations
  • supabase db reset on a local or branch database, which recreates tables from migrations

If your migrations never included grants (because they used to be automatic), a fresh environment built from them may now have tables your app can't reach.

Who is affected?

Supabase says this applies to projects that use the Data API, "Supabase's auto-generated REST and GraphQL layer, which is what supabase-js and our other client libraries call."

You're likely affected if:

  1. Your app calls Supabase from the browser or a mobile app with supabase-js.
  2. You build with an AI tool such as Lovable, Bolt, Cursor or v0 that creates tables for you.
  3. Your migrations create public tables with no GRANT statements.

You're not affected, per Supabase, if your app only talks to Postgres over a direct connection: psql, an ORM like Prisma or Drizzle, or a server with a connection string.

What does the 42501 error look like?

Supabase's changelog shows the response PostgREST returns when a grant is missing:

JSON

{
  "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;"
}

In your app, this usually shows up as a page that loads no data, a form that won't save, or a list that's always empty. The hint names the role and the privilege that's missing. Use that hint for that one table, not as a reason to grant everything.

What it looks like when we reproduced it

We reproduced this error on Oct 21, 2026 on a real Postgres engine (PGlite, which is Postgres compiled to WebAssembly), not a hosted Supabase project. We made Supabase-style anon and authenticated roles and a demo table, public.demo_todos, with 2 fake rows and no grants.

Terminal: setup with Supabase-style roles and a demo table that has no grants

Our setup: roles named like Supabase's and a demo table with 2 fake rows and no grants.

Terminal: anon reads the table and gets ERROR permission denied for table demo_todos, SQLSTATE 42501

Step 1: as anon, select * from public.demo_todos; fails with permission denied for table demo_todos (SQLSTATE 42501).

Terminal: fix part 1, grant usage on schema public, grant select to anon and authenticated, enable row level security

Step 2: grant usage on the public schema, grant select to anon and authenticated, and enable row level security.

Terminal: the same select now returns 0 rows and no error, because RLS is on with no policy

Step 3: the same query returns 0 rows and no error. RLS is on, but there's no policy yet.

Terminal: fix part 2, adding a select policy for anon where owner is demo-user-a

Step 4: add a select policy for anon (owner = 'demo-user-a').

Terminal: the select returns only the row the policy allows

Step 5: the same query returns only the allowed row.

Diagram: no grant gives error 42501, grant plus RLS with no policy gives 0 rows, and a policy gives only allowed rows

Our diagram of the three states we saw.

One caveat: this was plain Postgres with Supabase's role names, not Supabase's Data API (PostgREST). Through the API, the same 42501 error comes back as an HTTP error. We didn't test that part.

How do I fix permission denied for table in Supabase?

Decide who should use the table, then grant only that. Supabase's Securing your API (external) guide says to "grant the minimum privileges each role needs" and to "bundle grants with your RLS setup in the same migration."

A table only signed-in users use

SQL

create table public.notes (
  id bigint generated by default as identity primary key,
  user_id uuid not null default auth.uid(),
  body text
);

grant select, insert on public.notes to authenticated;
grant select, insert, update, delete on public.notes to service_role;

alter table public.notes enable row level security;

create policy "owners read their notes" on public.notes
  for select to authenticated using (auth.uid() = user_id);
create policy "owners write their notes" on public.notes
  for insert to authenticated with check (auth.uid() = user_id);

No grant to anon at all. Logged-out visitors can't reach the table, and signed-in users only see their own rows.

A table logged-out visitors should read

SQL

grant select on public.products to anon;
grant select on public.products to authenticated;
alter table public.products enable row level security;
create policy "public can read products" on public.products
  for select to anon, authenticated using (true);

Only select. Writes stay with your server code using service_role.

What not to do

Don't fix one error by granting everything to everyone:

SQL

-- Don't do this
grant all on all tables in schema public to anon;

That reaches every table, including ones that hold private data, and default-privilege variants of it also reach tables you create later. Grants decide whether a role can reach a table at all; RLS decides which rows it sees. You want both, set deliberately.

Why grants and RLS are separate layers

Supabase says: "RLS behavior remains unchanged. Grants are a separate layer: they control whether a role can access a table at all, while RLS controls which rows that role can see."

Think of it as two doors:

  • The grant is the front door to the table. No grant, and the API answers 42501.
  • RLS policies are the rules inside: which rows a user can read or change.

A grant with RLS turned off means anyone with that role can read every row. That's why the fix above always pairs the grant with enable row level security. If you're new to RLS, start with what Supabase RLS is.

A prompt to paste into your AI builder

If an AI tool writes your migrations, give it the rule once so every future table follows it:

Text

From October 30, 2026, Supabase no longer exposes new public tables to the Data API automatically.
For every new table:
1. In the SAME migration that creates it, add only the grants the app needs
   (authenticated for signed-in features; anon select ONLY if logged-out visitors truly need to read it;
   service_role for server code).
2. Enable Row Level Security and add policies for each operation.
3. Never grant ALL to anon, and never add a blanket "all tables in schema public" grant.
4. Apply the same to seed scripts, preview branches and db reset flows.
5. If you see error 42501, add the single GRANT from the error hint for that table and role.
Show me the migration before applying it.

Using Lovable or Bolt? See Lovable Supabase permission denied for the plain-English version.

How can I check my app before October 30?

Inside Supabase, run this in the SQL editor to see each table's grants:

SQL

select table_name, grantee, privilege_type
from information_schema.role_table_grants
where table_schema = 'public'
order by table_name, grantee;

Then check that every table you reach from the browser has RLS on (Table Editor shows it, or select relname, relrowsecurity from pg_class where relnamespace = 'public'::regnamespace;).

From the outside, our Oct 30 check looks at your live app the way a visitor's browser does. It finds your Supabase project URL and public key in your page, lists the tables the Data API shows to that key, and counts (without reading) whether each one returns rows. It never sends writes; if your API description offers writes, it says "possible" rather than testing them. Educational, not a security audit.

Also worth a read: is the Supabase anon key safe to ship? and the plain-English overview, will Supabase's Oct 30 change break your app?

FAQ

Will my Supabase app stop working on October 30? Not because of existing tables; Supabase says they keep their grants. Features that create new tables without a grant are what break.

Does this change expose my data? Not by itself. New tables become less reachable by default. The main risk is a broken feature, or a leak if someone fixes the error with a blanket grant and no RLS.

Do I still need RLS? Yes. Grants and RLS are separate, and you need both.

I use Prisma, Drizzle or psql. Am I affected? Per Supabase, direct Postgres connections aren't affected. The change applies to the Data API.

What does error 42501 mean? "Permission denied for table": most often the role making the request has no grant on that table, and the error's hint shows the grant to add. It can also come from an insert or update that fails an RLS with check rule, or from querying a schema that isn't exposed.

Should I just grant everything to anon to make the error go away? No. Grant select to anon only for data that's meant to be public, and protect it with RLS policies.

Do preview branches and db reset need grants too? Yes. Anything that recreates tables from your migrations needs the grants written into those migrations.

Why does 42501 happen on tables I just created? Because new public tables no longer get grants automatically once Supabase's change reaches your project (all existing projects on Oct 30, 2026). Older tables keep their grants, so only new ones fail until you add a grant in the same migration.

Does adding a GRANT fix Supabase error 42501 on its own? It fixes the error, but you may then see no rows. When we tested this on Postgres with Supabase-style roles, the grant plus RLS with no policy returned 0 rows and no error. The rows only appeared once we added a select policy, and then only the rows that policy allowed.

Sources

On this site

Related posts

Greenlight My App is independent and not affiliated with or endorsed by Supabase, PostgREST, Lovable, Bolt, Cursor, v0, Prisma or Drizzle. Product names are used only to describe compatibility. Educational, not a security audit. This page is general information, not professional security advice, legal advice, a security audit or a guarantee. Check your own setup against the vendor's current docs.