Get help

Check your Supabase migrations for missing GRANTs

Paste a migration (or a few). This finds new public tables that will hit 42501 permission denied after October 30, 2026, and writes the GRANT statements for you.

Your SQL never leaves your browser.

By default the fix grants authenticated and service_role only. A grant only counts as done when every role has the privileges it needs. Pasting several tables? The box applies to all of them, so check private and public tables in separate runs.

Comments and string literals are ignored. Tables in auth, storage, or other schemas are skipped.

What changes on October 30

On October 30, 2026, existing Supabase projects stop auto-granting privileges on new tables in the public schema. Until now, a CREATE TABLE in public quietly received SELECT, INSERT, UPDATE, and DELETE for anon, authenticated, and service_role. After the change, the table is there, your RLS policies can look fine, and the Data API still cannot see it.

The failure is Postgres error 42501, permission denied for table. PostgREST (the layer behind supabase-js) returns that code with a hint that names the missing GRANT. The app often surfaces it as a 401 or 403. This is not an RLS miss. Postgres checks grants first. If the role has no privilege on the table, policies never run.

Only new tables in the public schema created without GRANTs fail. Existing tables keep their current grants and stay reachable. New projects already dropped the old default (rollout from May 30, 2026). October 30 applies the same rule to every existing project.

Source: Supabase changelog (April 28, 2026) and the Securing your API docs. Same facts as our Oct 30 GRANT guide.

Who it affects

You are in scope if a Lovable app, or any other supabase-js client, creates tables in public and talks to them through the Data API (REST, GraphQL, or the client libraries). A typical Lovable migration does CREATE TABLE, ENABLE ROW LEVEL SECURITY, and CREATE POLICY, then stops. After October 30 that file ships a table nobody can reach from the app.

You are not in scope if you only use a direct Postgres connection (psql, an ORM, a connection string). storage, auth, realtime, and custom schemas keep their current defaults. Existing tables are not rewritten. The change is a default for future public tables, not a revoke on what you already have.

How to fix it by hand

Put the GRANT in the same migration as the table, RLS, and policies. Least privilege, not GRANT ALL on the whole schema. The usual app table looks like this:

alter table public.your_table enable row level security;

-- signed-in users: the operations your app actually uses
grant select, insert, update, delete on table public.your_table to authenticated;

-- server code / Edge Functions using the service_role key
grant select, insert, update, delete on table public.your_table to service_role;

-- only if signed-out visitors should read this table
-- grant select on table public.your_table to anon;

Leave anon with nothing unless signed-out visitors should read that table. A public posts table might get SELECT to anon. A profiles or invoices table should not. This checker leaves anon out by default. Tick the signed-out visitors box only for tables like that public posts table, and it adds SELECT for anon. If your app never deletes rows as a signed-in user, trim DELETE from the authenticated line too.

If you already see 42501, add a follow-up migration (or paste the GRANT into the SQL editor). Do not drop and recreate the table. Then retry the request. Skip GRANT ALL ON ALL TABLES IN SCHEMA public unless you really want every table opened, including ones with no RLS.

This page never uploads your SQL. There is no form post, no API call, and no analytics event with the paste. Check the file locally, copy the missing statements, and keep the rest of the migration as you wrote it.