AI & Technology

Supabase "Permission Denied" on New Tables: the October 30 Fix

Simon Dziak
Simon Dziak
Founder & developer, App369
LinkedIn
October 15, 2026•7 min

Key takeaways

  • On October 30, 2026, Supabase stops granting new tables in the public schema to anon, authenticated and service_role in existing projects. New projects have worked this way since May 30.
  • Tables you already have keep their grants and keep working. Only tables created after the change are affected.
  • A new table without grants fails with error 42501, "permission denied for table", in supabase-js, the REST API and GraphQL.
  • The fix is one grant per role in the same migration that creates the table, next to row-level security and its policies.
  • Grants decide whether a role can reach a table at all; RLS policies decide which rows it sees. You need both.

Pricing an app? Try the free app cost calculator or get a range for your project in the instant quote.

From October 30, 2026, a table you add to an existing Supabase project is invisible to your app until you grant it. Supabase used to give every new table in the public schema to the anon, authenticated and service_role roles automatically. It now leaves that to you. Tables you already have keep working; a table created after the change returns permission denied for table until its migration includes the grants.

To see which tables in your migrations are affected, paste them into the free Supabase October 30 grants checker. It runs in your browser and never uploads your SQL.

What changes, and when

Supabase announced the change in its changelog on April 28, 2026:

DateWhat happens
April 28, 2026New projects can opt out of exposing new tables when they are created
May 18, 2026pg_graphql is no longer enabled by default
May 30, 2026New projects stop exposing new tables by default (rolled out over a few weeks)
October 30, 2026The same default applies to all existing projects

What changes is Postgres's default privileges in the public schema. Before, every new table was granted select, insert, update, delete to anon, authenticated and service_role, new sequences got usage, select, and new functions got execute. After the change those defaults are gone, so the Data API (REST at /rest/v1 and GraphQL at /graphql/v1) can't see a new table until a role is granted on it.

What doesn't change:

  • Existing tables keep the grants they already have.
  • Row-level security works exactly as before.
  • Other schemas, such as auth, storage and realtime, aren't affected.
  • Direct Postgres connections (psql, an ORM or your own server) aren't affected. The change matters if your app talks to Supabase through supabase-js, REST or GraphQL.

The symptom

A request to a new table without grants fails with Postgres error 42501:

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

In supabase-js, the error object comes back filled and data is null. If the app doesn't show errors, users simply see an empty screen or a button that does nothing. The same query works in the SQL editor, which runs as postgres, the role that owns the table and has every privilege, and fails in the app. That is why this one can be confusing.

The fix: grant in the same migration

Put the grants in the migration that creates the table, so every environment (local, staging, production) ends up the same. This is the pattern from Supabase's Securing your API guide:

create table public.projects (
  id uuid primary key default gen_random_uuid(),
  user_id uuid not null references auth.users on delete cascade,
  name text not null
);

-- 1. Who can reach the table at all
grant select, insert, update, delete on table public.projects to authenticated;
grant select, insert, update, delete on table public.projects to service_role;

-- 2. Which rows they can see and change
alter table public.projects enable row level security;

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

create policy "Owners can add projects"
  on public.projects for insert
  to authenticated
  with check ((select auth.uid()) = user_id);

create policy "Owners can update their projects"
  on public.projects for update
  to authenticated
  using ((select auth.uid()) = user_id)
  with check ((select auth.uid()) = user_id);

create policy "Owners can delete their projects"
  on public.projects for delete
  to authenticated
  using ((select auth.uid()) = user_id);

A few details are easy to miss:

  • Grant only what each role needs. A table that signed-out visitors should never touch gets no anon grant at all. Public read-only data gets grant select ... to anon and nothing more.
  • serial columns need a sequence grant. If the table uses serial or bigserial rather than an identity column or a UUID, inserts also need grant usage, select on sequence public.projects_id_seq to authenticated;.
  • Functions called over the API need execute. For a function your app calls with rpc(), use grant execute on function public.your_function() to authenticated;. A security definer function runs with its owner's rights and bypasses RLS, so grant execute only to roles that should call it, and review those functions.
  • Grants are not a replacement for RLS. With a grant and RLS off, any signed-in user, or anyone at all if anon is granted, can read every row. Turn RLS on for every exposed table.

Check your project before October 30

  1. List your newest migrations (supabase/migrations/ in the repo, or the SQL your AI tool generated) and look for create table statements in public without matching grant lines.
  2. Run them through the grants checker. It lists each table that would need a grant, each table with RLS off, and each policy that lets anyone in.
  3. Test as a real user, not as postgres. Sign in to the app with a normal account and use each feature that touches a new table. The SQL editor bypasses grants, so it won't show the problem.
  4. Opt in early if you can. Supabase lets existing projects switch to the new default before October 30. The changelog gives the SQL to run in the SQL editor; it changes only tables and sequences created afterwards:
    alter default privileges for role postgres in schema public
      revoke select, insert, update, delete on tables from anon, authenticated, service_role;
    alter default privileges for role postgres in schema public
      revoke usage, select on sequences from anon, authenticated, service_role;
    

    Doing it on a staging project first shows you any missing grants on your schedule instead of the deadline's.

If you use Lovable, Bolt or another AI builder

These tools write SQL migrations for you, and nothing in a generated migration warns you that the defaults changed. Read each new migration before you approve it: every create table in public should be followed by a grant for each role that needs it and by enable row level security. If you can't read the SQL yourself, paste it into the checker; it flags missing grants in plain language.

If your AI-built app has more than a couple of these problems, or users are already seeing empty screens, App369's AI-Built App Rescue starts with a $450 Launch Check. Within 3 business days of read-only access, you get each issue it finds in sign-in, data access, payments and deploys, ranked, with where it is, plus a fixed price to fix the worst of them. The $450 is credited to that fix.

Frequently asked questions

Why does Supabase say "permission denied for table" on a table I just created?
Since the Data API change (May 30, 2026 for new projects, October 30, 2026 for existing ones), new tables in the public schema have no grants for anon, authenticated or service_role. Add the grants in the migration that creates the table.
Will my existing Supabase tables stop working on October 30?
No. Supabase says existing tables keep their current grants. Only tables created after the change need explicit grants.
Is this the same as a row-level security error?
No. A missing grant fails every request with "permission denied for table". RLS blocks differently: reads return no rows, updates and deletes on hidden rows change nothing, and a blocked insert fails with "new row violates row-level security policy".
Can I turn the change off for an existing project?
Supabase documents a rollback that restores the old default privileges, but that exposes every future table again. Granting each table on purpose is safer.
Does this affect Lovable, Bolt or other AI-built apps?
It affects any project that reaches new tables through the Data API (supabase-js, REST or GraphQL), whoever wrote the migrations, which covers most AI-built apps. Apps that only use a direct Postgres connection are not affected. Check new migrations for a grant per role before they run.

Sources

  1. Supabase changelog: tables not exposed to Data and GraphQL API automatically (April 28, 2026)
  2. Supabase docs: Securing your API
  3. PostgreSQL docs: GRANT

Want this done at a fixed price?

Fixed scope, fixed price, paid upfront. The work happens in writing; no call needed.

Tags
#supabase permission denied #supabase 42501 #supabase grants #supabase data api #supabase october 30 #supabase new tables #lovable supabase #row level security
Share:

Related Resources

Related Articles