Error de PostgreSQL: permission denied for table / schema
ERROR: permission denied for table orders
-- variante a livello di schema:
ERROR: permission denied for schema app
El rol con el que estás conectado carece del privilegio que la sentencia necesita: SELECT/INSERT/UPDATE/DELETE sobre una tabla, USAGE o CREATE sobre un schema, USAGE sobre una secuencia. (Las versiones más antiguas de PostgreSQL formulan la primera como "permission denied for relation".) La solución siempre es un GRANT — la parte interesante es por qué faltaba el grant.
Qué significa este error
Los privilegios de PostgreSQL son por objeto y no se propagan en cascada: para leer app.orders necesitas tanto USAGE sobre el schema app como SELECT sobre la tabla. Los propietarios sostienen implícitamente todos los privilegios sobre sus objetos — que es exactamente por qué todo funciona en desarrollo (donde la app se conecta como el propietario) y se rompe en producción (donde no lo hace).
Dos variantes adyacentes que conviene reconocer: permission denied for sequence orders_id_seq significa que el rol puede escribir la tabla pero no sacar IDs de su secuencia; permission denied for schema public empezó a aparecer ampliamente con PostgreSQL 15, que dejó de permitir por defecto que todos los usuarios crearan objetos en public.
Causas comunes
- Objetos creados por un rol distinto. Las migraciones se ejecutan como
migratoro un admin; la app se conecta comoapp_user; nadie concedió aapp_usernada sobre las nuevas tablas. - Los grants no son retroactivos hacia adelante:
GRANT ... ON ALL TABLES IN SCHEMAcubre solo las tablas existentes. Las tablas creadas la semana que viene no quedan cubiertas — para eso estáALTER DEFAULT PRIVILEGES. - La trampa de
ALTER DEFAULT PRIVILEGES: aplica solo a objetos creados por el rol para el que se estableció. Establecerlo como tú mismo mientras las migraciones se ejecutan como otro rol no hace nada. - Falta el
USAGEsobre el schema — los grants sobre tablas son inútiles si el rol no puede atravesar el schema. - PostgreSQL 15+:
CREATEsobre el schemapublicya no se concede a todos; herramientas que "simplemente funcionaban" antes de una actualización empiezan a fallar.
Cómo diagnosticarlo
SELECT current_user; -- quién eres de verdad (ojo con los poolers)
\dp orders -- privilegios y propietario de la tabla
\dn+ -- privilegios sobre los schemas
SELECT has_table_privilege('app_user', 'app.orders', 'SELECT');
Lee la salida de \dp sin piedad: una columna "Access privileges" vacía significa que solo el propietario tiene acceso. Y comprueba quién es el propietario — eso suele explicar cómo surgió la situación.
Cómo solucionarlo
-- El conjunto mínimo típico para un rol de aplicación:
GRANT USAGE ON SCHEMA app TO app_user;
GRANT SELECT, INSERT, UPDATE, DELETE ON ALL TABLES IN SCHEMA app TO app_user;
GRANT USAGE, SELECT ON ALL SEQUENCES IN SCHEMA app TO app_user;
-- Y para las tablas FUTURAS creadas por el rol de las migraciones:
ALTER DEFAULT PRIVILEGES FOR ROLE migrator IN SCHEMA app
GRANT SELECT, INSERT, UPDATE, DELETE ON TABLES TO app_user;
ALTER DEFAULT PRIVILEGES FOR ROLE migrator IN SCHEMA app
GRANT USAGE, SELECT ON SEQUENCES TO app_user;
-- PostgreSQL 15+, si hace falta crear objetos en public:
GRANT CREATE ON SCHEMA public TO app_user;
- Fíjate en el
FOR ROLE migrator: los default privileges deben asociarse a el rol que crea los objetos. - Alternativa estructural que adoptan muchos equipos: hacer que todos los objetos sean propiedad de un rol dedicado
app_owner, ejecutar las migraciones como él, y conceder al rol de runtime solo lo que necesita.
