Saltar para o conteúdo

Supabase 42501 permission denied: um grant, não o RLS

Supabase

Ver como MarkdownAbrir em ClaudeAbrir em ChatGPTAbrir em Perplexity

A pergunta também é copiada. Se o chat abrir vazio, cole-a.

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:

O que uma tabela nova responde depois de 30 de outubro de 2026. Supabase local com os novos privilégios por omissão, um pedido cada. Publishable key, sem grant: create table → sem grant → 42501, HTTP 401; Secret key, sem grant: create table → sem grant → 42501, HTTP 403; Depois de grant select e ligar o RLS: grant select → RLS ligado → 0 linhas, HTTP 200.
A minha reprodução numa stack local do Supabase com os privilégios por omissão do changelog do Supabase, 23 de outubro de 2026.
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.

© 2026 Denys Zaichenko. Artigo original: https://madsalt.tech/pt/notes/supabase-42501-permission-denied/