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

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;