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;
SUPABASE · 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
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
Source: Supabase changelog, tables not exposed to the Data and GraphQL API automatically.
02 / WHY THE APP BREAKS
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
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.
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;
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;
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
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
$149
$249
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
Grants for the right role. RLS left on. A look at the rest of the tables, so the next migration does not repeat this.