Supabase's October 30 change: fix error 42501 without opening your data
On October 30, 2026, Supabase stops exposing new tables to its Data API by default in existing projects. Apps built with Lovable, Bolt or Cursor will hit a permission error the first time they add a table, and the quickest fix an AI tool offers can make that table readable by anyone.
What changes
- From October 30, new tables you create in the
publicschema of an existing project can't be reached through the Data API (supabase-js,/rest/v1, GraphQL) until you grant access. Most new projects have worked this way since late May. - Tables you already have keep their grants and keep working.
- Apps that connect to Postgres directly are not affected.
Source: Supabase changelog.
What you'll see
The first query against a new table fails with:
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;"
The trap
Paste that error into an AI coding tool and it will usually do what the hint says, or go further and grant everything on every table.
A grant decides whether a role can reach a table. Row-level security (RLS) decides which rows it gets. If the table has no RLS, a grant to anon makes every row readable by anyone who opens your site, because the anon key ships in your JavaScript. Grant insert, update or delete as well, and the rows become writable too.
The safe fix
Do all three steps in the same migration. Replace your_table and user_id with your table and its owner column.
-- 1. Grant only what the app needs
grant select, insert, update, delete on public.your_table to authenticated;
-- Server code (Edge Functions) that uses the service role needs it too:
grant select, insert, update, delete on public.your_table to service_role;
-- Grant select to anon only if logged-out visitors must read this table.
-- 2. Turn on row-level security
alter table public.your_table enable row level security;
-- 3. Let each user reach only their own rows
create policy "owners manage their rows"
on public.your_table for all
to authenticated
using (auth.uid() = user_id)
with check (auth.uid() = user_id);
If you hand this to your AI tool, ask it to keep the three steps together and never to use using (true) on tables that hold user data.
Check the tables you already have
It takes two minutes. Run this in the Supabase SQL Editor:
select c.relname as table_name,
c.relrowsecurity as rls_enabled,
has_table_privilege('anon', c.oid, 'select') as anon_can_read,
has_table_privilege('authenticated', c.oid, 'select') as signed_in_can_read
from pg_class c
where c.relnamespace = 'public'::regnamespace
and c.relkind in ('r', 'p')
order by rls_enabled, table_name;
- rls_enabled false, anon_can_read true: anyone with your site's public key can read every row.
- rls_enabled false, signed_in_can_read true: anyone who signs up can read every row.
- rls_enabled true: the policies decide. Make sure none of them say
using (true)on user data.
Supabase's Security Advisor, in the dashboard, also flags the tables this change affects.
Want a second pair of eyes?
Liftoff Review checks this and the rest of an AI-built app before launch: RLS and policies, keys in the frontend, payment webhooks and open endpoints. Fixed price, results in 72 hours, and a paste-ready fix prompt for every finding.