---
title: "Supabase 42501 permission denied: un grant, no RLS"
description: "Por qué Supabase devuelve 42501 permission denied, qué cambia el 30 de octubre de 2026 y el grant, RLS y política que lo arreglan, reproducido en local."
url: "https://madsalt.tech/es/notes/supabase-42501-permission-denied/"
author: "Denys Zaichenko"
language: "es"
published: 2026-10-26
---

# Supabase 42501 permission denied: un grant, no RLS

En resumen

`42501 permission denied for table …` significa que el rol no tiene ningún grant sobre la tabla, así que Postgres detiene la petición antes de que se ejecute cualquier política RLS. Desde el 30 de octubre de 2026 Supabase deja de conceder automáticamente las tablas nuevas de `public` en los proyectos existentes, así que las tablas creadas después responden 42501 hasta que usted las conceda. La corrección son tres sentencias juntas: un grant para las operaciones que el rol necesita, `enable row level security` y una política. Conceder solo el grant, sin RLS, abre la tabla a todo el mundo.

Construí un [verificador de seguridad de Supabase](https://madsalt.tech/es/tools/supabase-security-check/) gratuito que lee los grants de cada tabla expuesta, y 42501 es el error que aparece cuando los grants fallan en la otra dirección: de menos en lugar de de más. Aquí está qué significa y qué vi al reproducirlo.

## ¿Qué significa el error 42501 en Supabase?

42501 es el código de Postgres para privilegio insuficiente. Postgres comprueba dos capas antes de que un cliente toque una tabla: los grants deciden si el rol puede ejecutar la operación, y las políticas RLS deciden qué filas. Un grant ausente falla primero, como un error. Una política que no coincide con nada falla en silencio, como una lista vacía. Así que cuando vea 42501, mire los grants, no sus políticas.

PostgREST, la Data API que hay detrás de `supabase-js`, pasa el error con una pista. En mi stack local (Supabase CLI 2.109.1, Postgres 17.6, PostgREST 14.14), una petición con la publishable key a una tabla sin grant para `anon` devolvió HTTP 401 y:

```json
{
  "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"
}
```

## ¿Por qué las tablas nuevas dan 42501 después del 30 de octubre de 2026?

Hasta ahora, una tabla nueva en `public` recibía `select`, `insert`, `update` y `delete` para `anon`, `authenticated` y `service_role` mediante privilegios por defecto, así que era accesible por la Data API en cuanto se creaba, estuviera protegida o no. El [changelog](https://supabase.com/changelog/45329-breaking-change-tables-not-exposed-to-data-and-graphql-api-automatically) de Supabase elimina esos valores por defecto: opcional desde el 28 de abril de 2026, por defecto en los proyectos nuevos desde el 30 de mayo de 2026 y aplicado a todos los proyectos existentes el 30 de octubre de 2026. Las tablas existentes conservan sus grants.

Qué más rompe el cambio y cómo revisar el proyecto antes: [Supabase, 30 de octubre: qué rompe el cambio de grants](https://madsalt.tech/es/blog/supabase-october-30-grant-change/).

Apliqué el mismo cambio a mi proyecto local con las dos sentencias `alter default privileges … revoke` del changelog y creé una tabla nueva, `invoices`:

![Qué responde una tabla nueva después del 30 de octubre de 2026. Supabase local con los nuevos privilegios por defecto, una petición cada una. Publishable key, sin grant: create table → sin grant → 42501, HTTP 401; Secret key, sin grant: create table → sin grant → 42501, HTTP 403; Tras grant select y activar RLS: grant select → RLS activado → 0 filas, HTTP 200.](https://madsalt.tech/charts/es/supabase-42501-permission-denied.svg)

*Mi reproducción en un stack local de Supabase con los privilegios por defecto del changelog de Supabase, 23 de octubre de 2026.*

| Petición a la tabla nueva                                          | Resultado                                                         |
| ------------------------------------------------------------------ | ----------------------------------------------------------------- |
| Publishable key, sin grant                                         | HTTP 401, 42501, pista `GRANT SELECT ON public.invoices TO anon;` |
| Secret key, sin grant                                              | HTTP 403, 42501, pista `… TO service_role;`                       |
| Publishable key, tras `grant select` y `enable row level security` | HTTP 200, `[]` hasta que una política permita filas               |

Dos cosas me sorprendieron. La secret key también es rechazada: `service_role` se salta RLS, pero aun así necesita un grant, así que una Edge Function o una tarea de servidor que escriba en una tabla nueva se rompe del mismo modo. Y el revoke solo quita esas cuatro operaciones: la tabla nueva siguió llegando con `REFERENCES`, `TRIGGER` y `TRUNCATE` para `anon`, `authenticated` y `service_role`. Ninguna política se aplica a `TRUNCATE`.

## ¿Cómo se corrige 42501 sin abrir la tabla?

Trate el grant, RLS y la política como un solo cambio, en una sola migración, y conceda a cada rol solo lo que necesita:

```sql
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);
```

El `revoke all` inicial limpia el `TRUNCATE`, `TRIGGER` y `REFERENCES` que llegaron con la tabla. No responda a 42501 con `grant all on all tables in schema public to anon`: eso es devolver el valor por defecto antiguo, para todas las tablas a la vez, y cualquier tabla sin RLS pasa a ser legible por cualquiera que tenga su clave pública.

Si una herramienta de IA crea sus tablas, dígale que emita los grants, RLS y las políticas en la misma migración. La pista del error está escrita para que un agente pueda corregir el grant por sí mismo, pero la pista nombra solo el grant, no el RLS que debería acompañarlo.

## ¿Cómo lo ve el verificador?

Cuatro de sus reglas leen la capa de grants: "Privilegios por defecto que abren objetos nuevos a la API" señala los proyectos que aún conceden las tablas nuevas automáticamente, "Permisos de escritura sin uso para visitantes anónimos" encuentra grants de escritura de `anon` que ninguna política usa, y "Privilegios de tabla innecesarios para usuarios con sesión" encuentra `TRUNCATE`, `TRIGGER` y `REFERENCES` como los de arriba. "Políticas que nunca se ejecutan: el rol no tiene grant" lista las tablas donde una política nombra un rol que no tiene grant para ese comando, que es el aspecto de una tabla nueva después del 30 de octubre. Por qué RLS sigue importando después del grant está en [cómo probar Supabase RLS](https://madsalt.tech/es/blog/how-to-test-supabase-rls/), y cómo se ven los grants y las políticas en toda una app está en mi [lista de seguridad para apps Lovable](https://madsalt.tech/es/blog/lovable-app-security-checklist/).
