31 October 2026 · 6 min read · BlitzHash Team · Supabase

Supabase "permission denied for table" after Oct 30: fix new tables without opening your data

If your app started showing errors or empty screens after you added a new feature this week, and the logs say "permission denied for table", you're probably seeing a Supabase change that reached every existing project on October 30, 2026. From that date, new tables created in the public schema are no longer reachable from your app until someone grants access to them. Older tables are untouched. This post explains the change in plain English, how to fix the error without opening your data to the world, and the one "quick fix" an AI builder might suggest that you should refuse.

If you'd like someone to check your access rules after the change, our Supabase RLS check reviews who can reach which tables and rows, and tests it as a stranger and as a second user. If you want to understand it first, read on.

What changed on October 30

Supabase announced this change on April 28, 2026 as a breaking change, rolled it out to new projects from May 30, and set October 30, 2026 as the date it applies to all existing projects. According to Supabase's changelog:

  • Before: every new table in public was automatically readable and writable through the Data API by the anon, authenticated and service_role roles. RLS rules then decided which rows each one could see.
  • After: a new table exists, but your app can't reach it through the Data API until there's an explicit grant for each role that needs it.
  • Existing tables keep their current access, so a working app shouldn't break overnight. It breaks the first time someone adds a new table.
  • RLS works the same as before. Grants decide whether a role can reach a table at all. RLS decides which rows it can see.

The Data API is what supabase-js and the other client libraries call. That's how almost every app built with Lovable, Bolt, v0 or Cursor talks to Supabase. Apps that only connect to Postgres directly, through a server and a connection string, aren't affected.

Supabase gives its reason plainly: AI tools, scripts and agents now create tables without a person reviewing each one, and the old default made new tables reachable before anyone had secured them.

How to tell if it's hitting you

You're likely affected if all three are true:

1. Your Supabase project is in your own account and was created before this change reached new projects. 2. You (or your AI builder) added a new table on or after October 30. 3. The feature that uses that table now fails, or shows nothing.

When a grant is missing, Supabase returns an error like this instead of failing silently:

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

You'll see it in the browser's developer tools (F12, then the Network tab, then click the failing request) or in your Supabase logs. Supabase's API keys guide adds a useful tip: a missing grant gives a permission error, while an RLS rule that matches nothing gives an empty result. Check the grant before you debug the rule.

If your app runs on a database your builder manages for you, rather than a Supabase project in your own account, check that builder's own changelog for how it handles this.

The quick fix to refuse

When an AI builder sees "permission denied", it may suggest something like:

grant all on all tables in schema public to anon;

and sometimes it also turns RLS off "to fix the error". Don't accept either. Supabase's own rollback instructions include a bulk grant to anon, authenticated and service_role, and they're careful to say you should then revoke access to anything you want kept private. That bulk grant is a way back to the old default, not a security fix. Once a role can reach a table, RLS is the only thing deciding which rows it sees. A grant to anon on a table with RLS off means anyone on the internet can read it, and possibly change it.

The safe fix, step by step

Supabase recommends treating three steps as one unit for every new table: grant, turn on RLS, add rules. Here's that in order, with a check at the end.

Step 1: Decide who should reach the table

For each new table, answer in plain words:

  • Signed-out visitors (`anon`): should they read it at all? Usually only for public content like a product list or blog.
  • Signed-in users (`authenticated`): read only, or read and write? Only their own rows?
  • Your server code (`service_role`): does a server function or scheduled job use this table?

Step 2: Grant only what each role needs

In the Supabase SQL Editor, for a table called invoices that only signed-in users touch:

grant select, insert, update, delete on public.invoices to authenticated;
grant select, insert, update, delete on public.invoices to service_role;
-- no grant to anon: strangers can't reach this table at all

Skip any line you don't need. A table signed-out visitors never use should have no grant to anon.

Step 3: Turn RLS on and add rules

alter table public.invoices enable row level security;

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

Add similar rules for insert, update and delete if users can do those. Change user_id to whatever column holds the owner in your table.

Step 4: Check every table in one query

This read-only query lists each table in public, whether RLS is on, and whether strangers and signed-in users can read it:

select c.relname as table_name,
       c.relrowsecurity as rls_on,
       has_table_privilege('anon', c.oid, 'select') as anon_can_select,
       has_table_privilege('authenticated', c.oid, 'select') as signed_in_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;

Look for any row where rls_on is false and either can_select column is true. That table is open. Fix it first.

Step 5: Test as a stranger and as a second user

Open your app in a private window without signing in and use the new feature. Then sign in as two different test accounts and make sure neither can see the other's rows. Our 30-minute RLS walkthrough covers this in more detail, and our stranger's-eye leak test shows how to ask each table for a row with only the public key. You can also run the free self-check.

Tell your AI builder about the new rule

Supabase suggests updating your AI tool's instructions (or using Supabase's agent skill) so table creation always includes grants. If your builder has a project knowledge or rules file, add something like:

"Whenever you create a table in Supabase, in the same change: grant only the privileges each role needs (never grant to anon unless signed-out visitors must read it), enable row level security, and add policies that limit each row to its owner. Never use grant all, and never disable RLS to fix a permission error."

This keeps every future table safe by default, rather than fixing them one at a time.

Why this change is good news

It's annoying when a new feature breaks, but this change closes one of the most common ways AI-built apps leaked data: a new table that was public for hours or weeks before anyone added rules. Supabase also lists tables worth reviewing in the Security Advisor and with a Data API exposure badge in the Table Editor. The error is now loud and comes with a hint, which beats a silent leak.

Want a second pair of eyes?

If you're not sure which roles each table needs, or your builder already applied a bulk grant, book a free technical assessment. Our Supabase RLS check reviews your grants and rules table by table and tests them the way a stranger would.

Sources

Related posts

Next step

Book a free technical assessment.

Thirty minutes. We look at where you are, whether you are about to build, ready to launch, or already live, and tell you what matters first. You leave with a written next step, whether or not you hire us.

Every price is a custom quote after your assessment. No work starts until you accept the proposal.