Oct 30: what broke, and the 2-minute fix
Supabase's changelog names October 30, 2026 as the day the new behavior is enforced on all existing projects: a new table in public is not reachable through the Data API until you grant it, and requests fail with 42501. Existing tables keep their grants, and apps that only use a direct Postgres connection are not affected.
What was reported
Supabase's changelog names "October 30, 2026, when the new behavior is enforced on all existing projects." Once it reaches your project, a new table in public is not reachable through the Data API (supabase-js, REST, and GraphQL) until you grant it. "Existing tables are not affected in your project, they keep their current grants and stay reachable," and an app that only uses a direct Postgres connection is not affected. For new projects, the default began rolling out on May 30, 2026. The changelog says: "this setting starts to become the default for all new projects. This will be a gradual rollout over a few weeks to monitor impact."
This post is the short version for one new table. How do I check and fix Supabase's October 30, 2026 Data API grants change? has the read-only queries for default privileges and table grants, how to tell a missing grant from a policy that matches no rows, and when a serial sequence or a view needs a grant of its own. Functions are outside this change: Can anyone call your Supabase functions? covers new functions, which stay callable by every role. Other Supabase reports, including Storage advisories, are on Supabase incidents.
What broke
A new table made in the SQL editor, a migration, the Management API, MCP, the CLI, or an AI coding tool now has no Data API access. Requests fail with 42501, which clients often report as 401 or 403, per Supabase's Database API 42501 errors page. The changelog shows the response:
{
"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 hint suggests a grant to anon. Do not paste it, or any blanket grant to anon. Grant each role only what it needs.
The 2-minute fix
This assumes your table has a user_id uuid references auth.users (id) column holding the owner's id. Replace your_table and run the block once:
begin;
alter table public.your_table enable row level security;
create policy "owners can read their rows"
on public.your_table for select
to authenticated
using ((select auth.uid()) = user_id);
create policy "owners can add their rows"
on public.your_table for insert
to authenticated
with check ((select auth.uid()) = user_id);
create policy "owners can update their rows"
on public.your_table for update
to authenticated
using ((select auth.uid()) = user_id)
with check ((select auth.uid()) = user_id);
create policy "owners can delete their rows"
on public.your_table for delete
to authenticated
using ((select auth.uid()) = user_id);
grant select, insert, update, delete on public.your_table to authenticated;
grant select, insert, update, delete on public.your_table to service_role;
commit;
- Row level security comes first. That order is ours, not Supabase's. The changelog's example lists the grant first and says "Treat these three steps as a unit." In one transaction nothing is exposed in between, so the order only matters if the statements run separately. If statements run separately, stop on the first error; enabling RLS successfully before granting access avoids an intermediate state with new Data API grants and RLS disabled.
- One policy per operation, as the Row Level Security docs say: "Write a separate policy for select, insert, update, and delete."
with checkon insert and update stops writes with someone else'suser_id. service_rolegets the grant too, as in the changelog's example. Securing your API calls it "Full access for server-side code using the service role." Without it, server code using the secret key gets 42501. The Row Level Security docs sayservice_rolebypasses row level security, so keep that key server-side.- This fix adds no Data API privileges for
anon. For a table signed-out visitors should read, grantselectonly, with a select policyto anon, which the Row Level Security docs reserve for "data that is meant to be public":
grant select on public.your_table to anon;
create policy "anyone can read"
on public.your_table for select
to anon
using (true);
Outside the SQL editor, run psql -X -v ON_ERROR_STOP=1 -f your_migration.sql. The psql docs say that with ON_ERROR_STOP set, on a script error "processing will instead stop immediately" and psql exits with code 3, so it never reaches commit;. Do not add --single-transaction: with begin; and commit; in the file, "this option will not have the desired effects." For migrations, see grants in migrations.
Supabase calls this "the next step in a series of platform default changes." Get the next one in your inbox the week it lands, with the fix SQL. One email a week, no spam, unsubscribe any time.
Check it in 30 seconds
This read-only query is the one from the 42501 page, with a table_schema filter we added. It lists each role's privileges on your table:
select grantee, privilege_type
from information_schema.role_table_grants
where table_name = 'your_table'
and table_schema = 'public';
After the fix, look for SELECT, INSERT, UPDATE, and DELETE for both authenticated and service_role. For anon, expect none of those four privileges unless you added the public-read block, which adds SELECT. The query can also show owner grants and retained privileges such as TRUNCATE, REFERENCES, and TRIGGER; those privileges do not let anon read the table through the Data API. To check every table and view in public at once, run the read-only query that lists each Data API role missing a grant.
The Supabase exposure checker counts the rows your anon key can read, table by table. It does not detect missing grants: a table that answers 42501 shows as inconclusive.
If an AI tool creates your tables
The changelog says: "If you use an AI coding tool to create tables, update its system prompt or adopt the Supabase agent skill, which includes the grants step."
Go deeper
- The full guide: fix SQL, 42501, rollback
- Am I affected? Two read-only queries
- Grants in your migrations
- Find tables without RLS
Know someone who ships on Supabase? Send them this page: https://shipsecurely.dev/feed/supabase-oct-30-what-broke
How to prevent it
For every new table in public that your app reaches through supabase-js, enable row level security, add one policy per operation, and grant authenticated and service_role in the same transaction or migration. Grant anon nothing unless signed-out visitors should read the table, and then only select.
Sources
- Supabase changelog: Tables not exposed to Data and GraphQL API automatically
- Supabase: Database API 42501 errors
- Supabase: Row Level Security
- Supabase: Securing your API
- PostgreSQL: psql
Related
Incidents on the same topics
- A Supabase Storage anon key could read and overwrite Iceberg analytics files
- Lovable-generated sites with missing RLS let unauthenticated users read or write tables
- Supabase Storage accepted replayed S3 signed requests and presigned URLs after they should have expired
- A crafted dyad:// link could connect a Dyad user's apps to an attacker's Supabase or Neon account
The weekly fix
The weekly fix: 3 real security mistakes in vibe-coded apps and how to fix them, every Tuesday.