Supabase error 42501 "permission denied for table": the safe fix for new tables
Your AI builder created a new table, and now the browser console shows a 42501 "permission denied" error. The fix is a few lines of SQL. The risky "fix" is turning Row Level Security off or granting everything to everyone.
Short answer: Supabase is changing its defaults so that new tables in the public schema are not reachable through the Data API until you grant access. Its changelog says this applies to all existing projects on October 30, 2026. Existing tables keep their grants. When you hit 42501 on a new table, grant only what each role needs, enable RLS and add policies in the same migration. Never disable RLS to make the error go away.
Here is the error as PostgREST returns it:
{
"code": "42501",
"message": "permission denied for table invoices",
"hint": "Grant the required privileges to the current role with: GRANT SELECT ON public.invoices TO anon;"
}
If your app uses supabase-js (as Lovable, Bolt and most Cursor-built React apps do) and the table is new, Supabase's new default is a likely cause. 42501 means the requesting role has no privilege on that table.
What changed, and when
Supabase announced the change in its changelog on April 28, 2026 (Breaking change: tables not exposed to Data and GraphQL API automatically). Per that post:
- Before: every new table in
publicwas automatically grantedselect,insert,updateanddeleteforanon,authenticatedandservice_role, so it was reachable through the Data API the moment it was created. - From May 30, 2026: the new behavior started becoming the default for new projects, rolled out gradually over a few weeks.
- October 30, 2026: Supabase says the setting will be applied to all existing projects.
Once it reaches your project, a new table in public is not reachable through the Data API (REST, GraphQL, supabase-js) until you explicitly grant access. Existing tables keep their current grants. If your app only talks to Postgres over a direct connection (an ORM or server with a connection string), Supabase says the change doesn't affect you. Dates are from Supabase's post as of October 21, 2026.
Why this is good news for AI-built apps
Supabase's reasoning: agents and AI platforms now create tables with no human reviewing the change, and auto-grants made it easy to expose a table before anyone secured it.
Grants and RLS are two separate layers. A grant decides whether a role can touch the table at all; an RLS policy decides which rows it can see or change. A missing grant fails before RLS is checked: that's 42501.
The safe fix: grant, enable RLS, add a policy [manual]
Treat these three steps as one unit, in the same migration. Replace invoices and user_id with your table and owner column.
-- 1. Grant only what each role needs
grant select, insert, update, delete on public.invoices to authenticated;
grant select, insert, update, delete on public.invoices to service_role;
-- Only if logged-out visitors genuinely need to read it:
-- grant select on public.invoices to anon;
-- 2. Turn on Row Level Security
alter table public.invoices enable row level security;
-- 3. Add policies per operation
create policy "owners read their invoices"
on public.invoices for select to authenticated
using ((select auth.uid()) = user_id);
create policy "owners insert their invoices"
on public.invoices for insert to authenticated
with check ((select auth.uid()) = user_id);
- Don't grant to
anonby default, only for public data. - Never "fix" it by disabling RLS. A granted table with RLS off can be read by anyone holding your public anon key, and that key is in your site's JavaScript by design. See Is the Supabase anon key safe?
- Avoid
using (true)on private data. It looks like a policy but lets every caller read every row.
Check who can reach a table [manual]
See current grants in the SQL Editor:
select grantee, privilege_type
from information_schema.role_table_grants
where table_schema = 'public' and table_name = 'invoices';
Supabase says the Table Editor's Data API exposure badge and the Security Advisor also list tables worth reviewing. For a full policy walkthrough, use the Supabase RLS checklist.
Tell your AI builder the new rule
Paste this into Lovable, Bolt or Cursor:
Whenever you create a table in the Supabase public schema, in the same migration: 1) GRANT only the privileges each role needs (authenticated and service_role; anon only if the data is meant to be public), 2) ENABLE ROW LEVEL SECURITY, 3) add one policy per operation scoped to auth.uid(). If you hit error 42501, add the missing GRANT. Never disable RLS and never use "using (true)" on private data. Show me the SQL before applying it.
Opt in before October 30 [manual]
Supabase documents these statements for adopting the new behavior early. They only change defaults for future tables and sequences; existing tables keep their grants:
alter default privileges for role postgres in schema public
revoke select, insert, update, delete on tables from anon, authenticated, service_role;
alter default privileges for role postgres in schema public
revoke usage, select on sequences from anon, authenticated, service_role;
The change only touches default privileges in public, not storage, auth or custom schemas.
What the free scan checks here (and what it doesn't)
The scan doesn't test tables, grants or RLS, and can't tell whether this change has reached your project. It reports a Supabase URL or anon key as info only, with a reminder to audit RLS yourself, and flags a service_role JWT or sb_secret_ key as critical. It also checks /.env, /.git/HEAD, HTTPS and four headers. Checking grants is [manual].
FAQ
- Will my live app break on October 30?
- Supabase says existing tables keep their current grants, so they should keep working. The change affects tables created after it reaches your project, so the first sign is usually a 42501 on a brand-new table.
- The error hint says GRANT SELECT ... TO anon. Should I run it?
- Only if logged-out visitors should read that table, like a product catalogue. For user data, grant to authenticated and protect rows with RLS policies. The hint names anon because that is the role your browser used.
- Does a clean ShipWatch scan mean my tables are safe?
- No. ShipWatch doesn't test table grants, RLS policies or this 42501 change. It looks at your live site from the outside, notes a Supabase URL or anon key as info only, and flags a leaked service_role JWT or sb_secret_ key. Use the SQL on this page to check your tables.