SUPABASE · ERROR 42501

Permission denied for table is error 42501

On October 30, 2026, Supabase stops exposing new tables to the Data API. A Lovable, Bolt, v0, or Replit app that adds a table and queries it gets permission denied for table, until you grant access on purpose and leave RLS on.

$149 fixed price · Grants and RLS policies · Other tables checked

01 / WHAT CHANGED

New tables are invisible to your app.

Supabase has two doors into Postgres. The Data API is the one your app uses: supabase-js, the REST API at /rest/v1, and GraphQL. The other door is a direct database connection. This change is about the first door.

Before, a new table in the public schema was automatically granted to anon, authenticated, and service_role. Create it, and the client could query it.

After October 30, 2026, that default is off for every existing project. The table is in Postgres. The Data API cannot see it. The request fails before Row Level Security gets a vote. New projects have worked this way since May 30, 2026.

Tables you already had keep their grants. The failure starts the next time something creates a table: a Lovable edit, a Bolt change, the SQL editor, or a migration with no GRANT.

{
  "code": "42501",
  "message": "permission denied for table your_table"
}

BEFORE / AFTER OCT 30

New table→Error 42501
Grants→You write them
Existing tables→Keep working
RLS→Unchanged

Source: Supabase changelog, tables not exposed to the Data and GraphQL API automatically.

02 / WHY THE APP BREAKS

The builder still expects the old default.

Lovable, Bolt, v0, and Replit generate supabase.from("orders").select() the moment they add a feature. They usually create the table without a GRANT. Before October 30, the query still worked. After October 30, that new table returns 42501, while every older table still loads.

A grant and a policy answer different questions. The grant decides whether a role may touch the table at all. RLS decides which rows. Postgres checks the grant first. A careful policy on a table with no grant still returns permission denied for table.

You are unaffected if the app only uses a direct Postgres connection from a server you control. Almost every app built in Lovable, Bolt, v0, or Replit uses the Data API, so this is the path that breaks.

03 / FIXES THAT MAKE IT WORSE

The error hint is not a security review.

PostgREST includes a hint that names the role it just rejected, often GRANT SELECT ON public.your_table TO anon when the page used the anon key. AI builders run the fastest statement that makes the error go away. These three show up in the wild. Do not run them to "fix" 42501.

Turning RLS off

Disabling Row Level Security does not create a missing grant. If a grant is already there, it deletes the only rule about which rows anyone can see.

Do not run

alter table public.your_table disable row level security;

Granting the table to anon

anon is the role behind the public key in your frontend. A broad grant on user data, invoices, or messages makes that table reachable by anyone who can load your site.

Do not run

grant all on table public.your_table to anon;

service_role in the browser

The service_role key skips every RLS policy. Shipped in the frontend, anyone can copy it and use your database as an admin.

Do not run

createClient(url, SERVICE_ROLE_KEY)

04 / THE CORRECT FIX

Grant the role, then keep RLS on.

Put the GRANT in the same migration as the table. For normal app data, grant authenticated, not anon. Enable RLS in that migration, and add policies that name the same role. A policy with no TO clause applies to public, which includes anon.

Grant anon only where strangers are supposed to read the rows, and only the privileges they need. Usually that is select on something you already meant to publish.

PRIVATE TABLE · SAME MIGRATION

create table public.notes (
  id uuid primary key default gen_random_uuid(),
  user_id uuid not null references auth.users (id),
  body text not null
);

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

alter table public.notes enable row level security;

create policy "read own notes"
  on public.notes for select
  to authenticated
  using (auth.uid() = user_id);

create policy "insert own notes"
  on public.notes for insert
  to authenticated
  with check (auth.uid() = user_id);

create policy "update own notes"
  on public.notes for update
  to authenticated
  using (auth.uid() = user_id)
  with check (auth.uid() = user_id);

create policy "delete own notes"
  on public.notes for delete
  to authenticated
  using (auth.uid() = user_id);

PUBLIC READ · ANON SELECT ONLY

grant select on public.posts to anon;

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

alter table public.posts enable row level security;

create policy "public reads published posts"
  on public.posts for select
  to anon
  using (published = true);

If the primary key is identity or serial, also grant usage and select on that sequence to the same role, or inserts fail with a different permission error. A uuid key with gen_random_uuid() does not need a sequence grant. service_role stays on the server. It is not how the browser gets past 42501.

05 / FIXED PRICE

We can write the grants for you.

THIS ERROR

Fix 42501 the right way

$149

  • GRANTs for the tables that return 42501, in the migration with the table
  • RLS left on, with policies for who should see each row
  • A check of your other tables for missing grants and the three unsafe fixes
Fix 42501 the right way
THE WIDER REVIEW

Security audit

$249

  • RLS, exposed keys, storage buckets, and auth
  • Written report in 48 hours and a 30-minute call
  • 100% credited toward any larger fix
Get the $249 audit

Both go through the same request form. For the $149 fix, say you are hitting 42501. We reply within 12 hours with next steps.

06 / FAQ

Questions we get about 42501.

Clear the error without publishing the table.

Grants for the right role. RLS left on. A look at the rest of the tables, so the next migration does not repeat this.