PostgreSQL Permission Denied for Relation: Fix the Right Grant

Ghazi · August 4, 2026

PostgreSQL reports permission denied for relation when the current role lacks a required object privilege. The relation might be a table, view, sequence, or materialized view, and the missing schema permission may be reported separately.

Identify the current login, object owner, schema, and exact operation. Grant only the required schema and object privileges. If future objects must inherit access, set default privileges for the role that creates those objects rather than for the role receiving access.

Inspect identity, ownership, and effective privileges

select current_user, session_user;

select
  n.nspname as schema_name,
  c.relname,
  c.relkind,
  pg_get_userbyid(c.relowner) as owner,
  has_schema_privilege(current_user, n.oid, 'USAGE') as schema_usage,
  has_table_privilege(current_user, c.oid, 'SELECT') as can_select,
  has_table_privilege(current_user, c.oid, 'INSERT') as can_insert
from pg_class c
join pg_namespace n on n.oid = c.relnamespace
where n.nspname = 'app' and c.relname = 'orders';

Change app.orders to the object named in the error. relkind distinguishes tables, views, sequences, and other relation types.

Grant access at the correct layers

USAGE on a schema lets a role resolve objects inside it. It does not grant access to the tables. Likewise, SELECT on a table does not grant schema USAGE. Both may be needed.

INSERT into a table backed by a sequence can also require USAGE on that sequence. This commonly appears after a table grant seems to fix reads but inserts still fail.

Grant a read-write application role

grant usage on schema app to app_runtime;
grant select, insert, update, delete
  on all tables in schema app to app_runtime;
grant usage, select
  on all sequences in schema app to app_runtime;

Run grants as the object owner or another authorized role. Remove write privileges the application does not need.

Do not solve it with superuser

Making an application login a superuser hides the privilege error by bypassing normal controls. It also gives that application the ability to read, alter, or destroy unrelated data and configuration.

Use a group role when several logins need the same access. Grant privileges to the group, grant membership to each login, and test with SET ROLE or a direct connection as the runtime user.

Set defaults for objects created later

ALTER DEFAULT PRIVILEGES affects objects created in the future by a particular owner. It does not repair existing tables, and running it as the receiving application role will not affect tables later created by a migration owner.

Grant future objects created by the migration role

alter default privileges for role app_migrator in schema app
  grant select, insert, update, delete on tables to app_runtime;

alter default privileges for role app_migrator in schema app
  grant usage, select on sequences to app_runtime;

Run the command as app_migrator or a role allowed to alter its default privileges.

Separate RLS from object privileges

Row-level security is checked after ordinary object privileges. A missing SELECT privilege produces a permission error. A valid SELECT with a restrictive RLS policy can return fewer rows or reject a write instead.

Test the exact application login in a separate connection. For a stricter production setup, use the role patterns in secure PostgreSQL access for AI agents as a general least-privilege checklist.