dataiotech Labs
dbaguidedbaguideDBAdataxtoolsdataopsphere
Home/Error Library/postgresql-permission-denied-for-table
PostgreSQL
PostgreSQL 14–18Security & Privileges
7 min read
ERROR: permission denied for table / relation (SQLSTATE 42501)

PostgreSQL "permission denied for table" — Which Role, Which Layer is Missing?

Diagnosing permission denied across 4 layers: schema USAGE, table DML, sequence USAGE, and search_path resolution. Breaking changes in PostgreSQL 15 public schema.

Reviewed by:Principal Database Reliability Architect
Tested Versions: PostgreSQL 18.6, 17.11 & 15.6 (Alpine Linux / Docker 29.7) | Tarihsel: PostgreSQL 14 (public schema grant farkı)

Operational Verification & Reliability Metadata

Last Technical Review: 30 Ekim 2026
Tested VersionsPostgreSQL 18.6, 17.11 & 15.6 (Alpine / Ubuntu) | PostgreSQL 14.10 (öncesi/sonrası public CREATE)
Required PrivilegesObject/schema owner or SUPERUSER (to execute GRANT / ALTER DEFAULT PRIVILEGES)
Risk Assessment
MediumGranting overly broad GRANT ALL breaks least privilege principles and introduces audit vulnerabilities
Expected Outputhas_schema_privilege, has_table_privilege, and has_sequence_privilege all evaluating to true
Rollback Procedure:REVOKE excessive privileges and enforce structured role grants via ALTER DEFAULT PRIVILEGES FOR ROLE
PostgreSQL Docs: Privileges & Default Privileges
First 60 Seconds Emergency Protocol:

The error string is a symptom, not the root cause. Do not restart the server blindly. Run the diagnostic queries below to pinpoint the actual failure mode.

▸Symptom

plaintext
ERROR: permission denied for table siparis ERROR: permission denied for sequence siparis_id_seq ERROR: permission denied for schema public ERROR: permission denied for relation siparis

SQLSTATE error code: 42501 (insufficient_privilege).

Four distinct object types represent four distinct authorization layers. Because they all begin with the generic phrase "permission denied", developers frequently resort to sweeping GRANT ALL commands, introducing severe security vulnerabilities while still failing if the underlying sequence layer was overlooked.

IMPORTANT
PostgreSQL 15 Breaking Security Change: In PostgreSQL 15, default schema permissions were substantially tightened: The default CREATE privilege on the public schema was revoked from the PUBLIC pseudo-role. Database migration scripts that operated seamlessly on PostgreSQL 14 immediately fail with permission denied for schema public upon upgrading. This architectural change accounts for the majority of recent occurrences of this error.

▸Diagnostic Queries to Confirm Root Cause

Verify the effective runtime role and search path configured by your pooler or ORM:

sql
SELECT current_user, session_user, current_setting('search_path');

Next, rather than manually parsing system catalogs, query PostgreSQL's built-in permission evaluation functions:

sql
SELECT has_schema_privilege('app_user', 'public', 'USAGE') AS schema_usage, has_table_privilege('app_user', 'public.siparis', 'SELECT') AS table_select, has_table_privilege('app_user', 'public.siparis', 'INSERT') AS table_insert, has_table_privilege('app_user', 'public.siparis', 'UPDATE') AS table_update, has_sequence_privilege('app_user', 'public.siparis_id_seq', 'USAGE') AS seq_usage;

Any column returning false identifies the exact missing authorization layer.


▸The Four Authorization Layers and Common Traps

1. Schema USAGE Privilege

Without USAGE on the parent schema, no table, view, or sequence contained within can be accessed. Even with GRANT ALL on a table, missing schema USAGE will fail the query:

sql
GRANT USAGE ON SCHEMA public TO app_user;

2. Table DML Privileges

SELECT, INSERT, UPDATE, DELETE. Trap: Executing an UPDATE statement requires evaluating WHERE clause column values; granting UPDATE without granting SELECT causes runtime query failure.

3. Sequence Privileges — The Most Overlooked Layer!

When inserting into tables with serial or GENERATED ALWAYS/BY DEFAULT AS IDENTITY columns, table privileges alone are insufficient. The backend must consume the sequence, requiring USAGE, SELECT on the sequence object:

sql
GRANT USAGE, SELECT ON SEQUENCE public.siparis_id_seq TO app_user;

4. The search_path Resolution Trap

If identically named tables exist across multiple schemas, PostgreSQL resolves unqualified references using search_path:

sql
SELECT to_regclass('siparis');

If this resolves to an unauthorized table earlier in the search path, the issue is not permissions, but schema precedence.


▸Step-by-Step Production Resolution

1. Grant Least-Privilege Access

sql
GRANT USAGE ON SCHEMA public TO app_user; GRANT SELECT, INSERT, UPDATE, DELETE ON public.siparis TO app_user; GRANT USAGE, SELECT ON ALL SEQUENCES IN SCHEMA public TO app_user;

2. Secure Future Objects with Default Privileges

Permissions granted today do not apply to tables created tomorrow by migration tools:

sql
ALTER DEFAULT PRIVILEGES FOR ROLE app_owner IN SCHEMA public GRANT SELECT, INSERT, UPDATE, DELETE ON TABLES TO app_user; ALTER DEFAULT PRIVILEGES FOR ROLE app_owner IN SCHEMA public GRANT USAGE, SELECT ON SEQUENCES TO app_user;
WARNING
The FOR ROLE app_owner clause is mandatory. Default privileges only take effect when objects are created by that specific role. If migrations execute as the postgres superuser, you must define default privileges FOR ROLE postgres.

3. Separate Schema Owners from Application Users

Maintain app_owner for schema creation/DDL and app_user strictly for DML operations. This separation guarantees that application vulnerabilities cannot execute unauthorized DROP TABLE or TRUNCATE operations.


▸Related Error Guides & Hub Links

Other Database Error Triage Playbooks

Other battle-tested diagnostic guides for high-severity database errors.

All Errors (50)