PsProdseal

Guides · Updated October 15, 2026

Supabase’s October 30 change: how to fix “permission denied” without opening your data

Supabase changes on Oct 30: don’t fix it the risky way.

On October 30, 2026, Supabase starts applying a new default to existing projects: new tables are no longer exposed to its Data API automatically. If your app was built with Lovable, Bolt, Replit or v0 and uses Supabase, you may notice the first time a new table is created after the change reaches your project. This guide explains what changes, what the error looks like, and how to fix it so only the right people can read your data.

What changes on October 30

Until now, every new table in the public schema was reachable through the Data API as soon as it was created. Supabase granted access to three roles by default: anon (logged-out visitors), authenticated (signed-in users) and service_role (your server). After the change, a new table in public exists in your database but can’t be reached through the Data API, which is what supabase-js calls, until you grant access to it yourself.

Tables you already have keep their current grants and stay reachable. Apps that talk to Postgres over a direct connection are not affected, and neither are tables in the storage, auth and realtime schemas. The new behavior became the default for new projects in a gradual rollout starting May 30, 2026, so a project created since then may already work this way.

What you will see

When your app reads or writes a table that has no grant, the request fails with error code 42501:

{
  "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 the app itself this can look like a page that loads empty or a save button that does nothing. Note the hint: it names the role that made the request. If you were logged out, it suggests a grant to anon, which is often not what you want for private data.

The quick fixes to avoid

When an AI builder sees this error, it often reaches for the fastest way to make it go away: granting every permission on the table to anon, or turning off row level security (RLS).

Both make the error disappear, and either one can make the table readable by anyone. Your anon key is public by design. It ships in the JavaScript that every visitor’s browser downloads, so anyone can copy it and query any table that anon can reach. A table that anon can read with RLS off, or with a policy like using (true), is effectively a public spreadsheet.

The safe fix

Grants and RLS are two separate layers. A grant decides whether a role can touch the table at all. RLS decides which rows that role can see. You want both: a narrow grant, and a policy that limits each user to their own rows.

For a table that holds per-user data, such as notes, orders or profiles, run this in the Supabase SQL Editor and replace your_table with the table’s name:

-- Let signed-in users use the table
grant select, insert, update, delete on public.your_table to authenticated;

-- Turn on row level security
alter table public.your_table enable row level security;

-- Each user can read only their own rows
create policy "users read their own rows"
  on public.your_table for select to authenticated
  using (auth.uid() = user_id);

-- Each user can add rows only for themselves
create policy "users add their own rows"
  on public.your_table for insert to authenticated
  with check (auth.uid() = user_id);
  • This assumes the table has a user_id column that stores the owner’s ID. Add update and delete policies the same way when your app needs them.
  • If an edge function or your server uses the service_role key on this table, grant to service_role as well.
  • Grant to anon only when logged-out visitors are meant to see the data, such as a public product list, and even then grant select only.

Asking your AI builder for help? Paste this guide into the chat and ask for grants to authenticated and RLS policies scoped to auth.uid(), with no grants to anon unless the table is meant to be public.

Test it with two accounts

A policy that looks right can still leak, so test it the way an outsider would.

  1. Create two test accounts, A and B.
  2. Sign in as A and add a row.
  3. Sign in as B and confirm that A’s row does not appear anywhere in the app.
  4. Sign out completely and confirm that nothing from the table shows up.

Repeat this for every new table you add after the change.

Check the tables you already have

The October 30 change only affects new tables. Tables created before it keep whatever access they had, including broad grants or missing policies. Supabase’s Security Advisor flags tables affected by the change, and the Data API exposure badge in the Table Editor shows which tables the API can reach. Our guide to checking RLS on every table has SQL that lists them all.

Prodseal can help with the rest. The free check looks at what your public app gives away to any browser, such as secret keys in your JavaScript. Verify that the app is yours by connecting GitHub, or Supabase with read-only access, and you get the full report for free: database access rules, logins and admin checks, Stripe webhooks, API routes, keys in your git history, storage buckets and AI calls. If you want the issues fixed, it’s $49 one time: tested fixes for what Prodseal can fix automatically, sent as a pull request you review (or as SQL to paste in), plus manual steps for the rest.

See what your app leaves open

Paste your app’s URL. The check is free and usually takes seconds.

Run my free check

Source: Supabase changelog, “Breaking Change: Tables not exposed to Data and GraphQL API automatically”, which also has the SQL for opting in early and for rolling back.

Supabase Oct 30 change: fix “permission denied” safely — Prodseal