PostgreSQL error: permission denied for table / schema

ERROR:  permission denied for table orders

-- variante a livello di schema:
ERROR:  permission denied for schema app

The role you are connected as lacks the privilege the statement needs: SELECT/INSERT/UPDATE/DELETE on a table, USAGE or CREATE on a schema, USAGE on a sequence. (Older PostgreSQL versions phrase the first one as "permission denied for relation".) The fix is always a GRANT — the interesting part is why the grant was missing.

What this error means

PostgreSQL privileges are per-object and do not cascade: to read app.orders you need both USAGE on schema app and SELECT on the table. Owners implicitly hold all privileges on their objects — which is exactly why everything works in development (where the app connects as the owner) and breaks in production (where it doesn't).

Two adjacent variants worth recognizing: permission denied for sequence orders_id_seq means the role can write the table but not draw IDs from its sequence; permission denied for schema public started appearing widely with PostgreSQL 15, which stopped letting every user create objects in public by default.

Common causes

How to diagnose it

SELECT current_user;                -- chi sei davvero (occhio ai pooler)

\dp orders                          -- privilegi e proprietario della tabella
\dn+                                -- privilegi sugli schemi

SELECT has_table_privilege('app_user', 'app.orders', 'SELECT');

Read \dp output ruthlessly: an empty "Access privileges" column means only the owner has access. And check who the owner is — that usually explains how the situation arose.

How to fix it

-- Il set minimo tipico per un ruolo applicativo:
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;

-- E per le tabelle FUTURE create dal ruolo delle migrazioni:
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+, se serve creare oggetti in public:
GRANT CREATE ON SCHEMA public TO app_user;