Guide
Supabase 42501 “permission denied for table” after 30 October: the GRANT fix
Short answer: on 30 October 2026 Supabase applies a new default to all existing projects: new tables you create in the public schema are no longer reachable through the Data API (supabase-js, REST, GraphQL) until you add an explicit Postgres GRANT.¹ Tables you already have keep their current grants and keep working.¹ If your AI builder adds a table after that date and your app suddenly gets 42501 permission denied for table ..., add a narrow grant for that table, make sure RLS is on, and write policies. Don't paste a blanket grant to anon.
Who this affects
Per Supabase's changelog, you need to act if your app reads or writes public tables through the Data API (which is what supabase-js does), and your migrations, CLI scripts or AI coding tool create tables without explicit GRANT statements.¹ That describes most Lovable, Bolt and Cursor apps that use Supabase.
You're unaffected if you only talk to Postgres over a direct connection (psql, an ORM, or a server with a connection string).¹ Tables in the auth, storage, realtime and custom schemas keep their current grants and defaults.¹
Timeline from the changelog:¹
| Date | What happens |
|---|---|
| 28 Apr 2026 | Opt-in available when creating a new project |
| 30 May 2026 | New behaviour becomes the default for new projects (gradual rollout) |
| 23 Oct 2026 | Supabase's final email notice to project owners and admins |
| 30 Oct 2026 | Enforced on all existing projects. New public tables without an explicit grant aren't reachable from the Data API |
42501 or an empty array? Tell grants and RLS apart
These two look similar in a broken app, but they have different fixes:
| What you see | What it means | Fix |
|---|---|---|
Error 42501 with "permission denied for table your_table" |
The role (anon or authenticated) has no GRANT on the table. Postgres rejects the query before RLS is even checked.¹ |
Add the grant (below). |
An empty array [] and no error on a select |
The grant is fine, but RLS is on and no policy matches this user. Once RLS is on, nothing is readable with the publishable key until you create policies.³ | Add or fix a select policy. Don't touch grants. |
Error 42501 with "new row violates row-level security policy" on an insert or update |
Grant is fine; RLS blocked the write.² | Add or fix the insert / update policy (its with check). |
So: 42501 "permission denied" = missing GRANT. Empty array = RLS on with no matching policy. Read the error message before you change anything. The hint in the error even names the role and privilege that's missing.¹ Supabase notes these errors often show up in clients as 401 or 403.²
{
"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 fix: one migration per new table
Grants decide whether a role can touch the table at all; RLS decides which rows it sees.¹ Supabase says to treat grant, RLS and policies as one unit.¹ Replace your_table with your table name and grant only what each role needs:
-- 1. Grants: only the privileges each role actually needs
grant select, insert, update, delete on table public.your_table to authenticated;
-- only if your Edge Functions or server code use the service_role key on this table:
grant select, insert, update, delete on table public.your_table to service_role;
-- 2. Turn on Row Level Security
alter table public.your_table enable row level security;
-- 3. Policies: one per operation, tied to the signed-in user
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);
Signed-out visitors need it too? Only for genuinely public data (a product catalogue, published posts), and usually read-only:
grant select on table public.your_table to anon;
create policy "anyone can read published rows" on public.your_table
for select to anon using (published = true);
If inserts then fail with "permission denied for sequence", the table uses an old-style serial id. Grant the sequence too: grant usage, select on sequence public.your_table_id_seq to authenticated; (Supabase's own opt-in script covers sequences alongside tables.¹)
Warning: never grant to
anonon a table without RLS. Your publishable (anon) key is public: anyone can read it from your site's JavaScript. A grant toanonon a table with RLS off lets anyone on the internet read, and withinsert/update/deletechange, every row. Always runenable row level securityin the same migration as the grant. Also don't paste Supabase's bulk rollback (grant ... on all tables in schema public to anon, authenticated, service_role) as a quick fix: it re-exposes every table, including ones you meant to keep private.¹
Prompt to give your AI builder (Lovable, Bolt, Cursor): "Whenever you create a table in Supabase, in the same migration: add explicit GRANTs for only the roles that need it (no grant to anon unless signed-out users must read it), enable Row Level Security, and create a policy for each operation using auth.uid(). Never grant to anon on a table without RLS."
Check what a table currently grants
select grantee, privilege_type
from information_schema.role_table_grants
where table_schema = 'public' and table_name = 'your_table';
(Adapted from Supabase's 42501 troubleshooting guide.²) If anon or authenticated is missing the privilege your app uses, that's your 42501.
Before 30 October
- Open Security Advisor in the Supabase dashboard. Until the switch, it flags affected tables per project and shows the remediation SQL.¹
- Update your AI builder's instructions (the prompt above), so new tables come with grants, RLS and policies.
- Want to test early? Supabase documents how to opt in on an existing project now, plus a rollback, in the changelog.¹ Try it on a staging project first.
- Test the app while signed out: anything a stranger shouldn't see should return nothing or fail.
- See our pre-launch Supabase RLS checklist for the rest.
FAQ
Will my existing tables break on 30 October? No. Per Supabase, existing tables keep their current grants and stay reachable. The change affects new tables created in the public schema after it reaches your project.
Why does my query return an empty array instead of an error? That's Row Level Security, not grants: RLS is on and no policy matches the current user, so Postgres returns no rows. Add or fix a select policy. A 42501 "permission denied for table" error means a GRANT is missing.
Is it safe to grant select to anon? Only on tables that have RLS enabled and a policy that limits rows to what's genuinely public. Never grant to anon on a table without RLS, because the anon (publishable) key is visible to everyone.
¹ Supabase changelog, "Breaking Change: Tables not exposed to Data and GraphQL API automatically" (checked 21 Oct 2026): https://supabase.com/changelog/45329-breaking-change-tables-not-exposed-to-data-and-graphql-api-automatically ² Supabase docs, "Database API 42501 errors": https://supabase.com/docs/guides/troubleshooting/database-api-42501-errors ³ Supabase docs, "Row Level Security": https://supabase.com/docs/guides/database/postgres/row-level-security