Em resumo
42501 permission denied for table … significa que a função não tem nenhum grant sobre a tabela, por isso o Postgres pára o pedido antes de qualquer política de RLS correr. A partir de 30 de outubro de 2026, o Supabase deixa de conceder automaticamente as tabelas novas de public nos projetos existentes, por isso as tabelas criadas depois respondem 42501 até lhes dar o grant. A correção são três instruções juntas: um grant para as operações de que a função precisa, enable row level security e uma política. Conceder só o grant, sem RLS, abre a tabela a toda a gente.
Criei um verificador de segurança do Supabase gratuito que lê os grants de todas as tabelas expostas, e o 42501 é o erro que aparece quando os grants falham no sentido contrário: a menos em vez de a mais. Aqui fica o que significa e o que vi ao reproduzi-lo.
O que significa o erro 42501 no Supabase?
42501 é o código do Postgres para privilégio insuficiente. O Postgres verifica duas camadas antes de um cliente tocar numa tabela: os grants decidem se a função pode executar a operação, e as políticas de RLS decidem que linhas. Um grant em falta falha primeiro, como um erro. Uma política que não corresponde a nada falha em silêncio, como uma lista vazia. Por isso, quando vir 42501, olhe para os grants, não para as políticas.
O PostgREST, a Data API por trás do supabase-js, passa o erro com uma dica. Na minha stack local (Supabase CLI 2.109.1, Postgres 17.6, PostgREST 14.14), um pedido com a publishable key a uma tabela sem grant para anon devolveu HTTP 401 e:
{
"code": "42501",
"details": null,
"hint": "Grant the required privileges to the current role with: GRANT SELECT ON public.orders TO anon;",
"message": "permission denied for table orders"
}
Porque é que as tabelas novas dão 42501 depois de 30 de outubro de 2026?
Até agora, uma tabela nova em public recebia select, insert, update e delete para anon, authenticated e service_role através de privilégios por omissão, por isso era alcançável pela Data API no momento em que era criada, estivesse protegida ou não. O changelog do Supabase remove esses valores por omissão: opcional desde 28 de abril de 2026, por omissão nos projetos novos desde 30 de maio de 2026, e aplicado a todos os projetos existentes a 30 de outubro de 2026. As tabelas existentes mantêm os grants.
O que mais a mudança parte e como verificar o projeto antes: Supabase, 30 de outubro: o que falha com os novos grants.
Apliquei a mesma alteração ao meu projeto local com as duas instruções alter default privileges … revoke do changelog e criei uma tabela nova, invoices:
| Pedido à tabela nova | Resultado |
|---|---|
| Publishable key, sem grant | HTTP 401, 42501, dica GRANT SELECT ON public.invoices TO anon; |
| Secret key, sem grant | HTTP 403, 42501, dica … TO service_role; |
Publishable key, depois de grant select e enable row level security |
HTTP 200, [] até uma política permitir linhas |
Duas coisas surpreenderam-me. A secret key também é recusada: o service_role ignora o RLS, mas continua a precisar de um grant, por isso uma Edge Function ou uma tarefa de servidor que escreva numa tabela nova parte-se da mesma maneira. E o revoke retira apenas essas quatro operações: a tabela nova continuou a vir com REFERENCES, TRIGGER e TRUNCATE para anon, authenticated e service_role. Nenhuma política se aplica ao TRUNCATE.
Como corrigir o 42501 sem abrir a tabela?
Trate o grant, o RLS e a política como uma só alteração, numa só migração, e dê a cada função apenas o que ela precisa:
revoke all on table public.invoices from anon, authenticated;
grant select, insert, update on table public.invoices to authenticated;
grant select, insert, update, delete on table public.invoices to service_role;
alter table public.invoices enable row level security;
create policy "Owners read their invoices"
on public.invoices for select
to authenticated
using ((select auth.uid()) = user_id);
O revoke all inicial limpa o TRUNCATE, o TRIGGER e o REFERENCES que vieram com a tabela. Não responda ao 42501 com grant all on all tables in schema public to anon: isso é repor o antigo valor por omissão, para todas as tabelas de uma vez, e qualquer tabela sem RLS passa a ser legível por quem tiver a sua chave pública.
Se uma ferramenta de IA cria as suas tabelas, diga-lhe para emitir os grants, o RLS e as políticas na mesma migração. A dica do erro está escrita para que um agente consiga corrigir o grant sozinho, mas a dica nomeia só o grant, não o RLS que devia vir com ele.
Como é que o verificador vê isto?
Quatro das suas regras leem a camada dos grants: “Privilégios por omissão que abrem novos objetos à API” assinala os projetos que ainda concedem as tabelas novas automaticamente, “Permissões de escrita sem uso para visitantes anónimos” encontra grants de escrita do anon que nenhuma política usa, e “Privilégios de tabela desnecessários para utilizadores com sessão iniciada” encontra TRUNCATE, TRIGGER e REFERENCES como os de cima. “Políticas que nunca correm: a função não tem grant” lista as tabelas em que uma política nomeia uma função sem grant para esse comando, que é o aspeto de uma tabela nova depois de 30 de outubro. Porque é que o RLS continua a importar depois do grant está em como testar o Supabase RLS, e o aspeto dos grants e das políticas numa app inteira está na minha lista de segurança para apps Lovable.