42501ERRORTier 1 — Safe✅ HIGH confidencepermission denied for table
What this means
The current role does not have the required privilege (SELECT, INSERT, UPDATE, DELETE, or TRUNCATE) on the named table or view. Postgres checks privileges at execution time against the pg_class and pg_namespace ACL entries.
Why it happens
- 1Role has not been granted SELECT or the required DML privilege on the table
- 2Role was granted privileges on an old version of the table but it was dropped and recreated (privileges do not transfer)
- 3Accessing a table through a view where the view's owner lacks the underlying table privilege
- 4Row-level security policy blocks access (raises a different message but same code in some cases)
- 5Schema-level USAGE privilege missing even if table-level SELECT is granted
How to reproduce
A role with no privileges attempts to query a table.
CREATE ROLE readonly_user LOGIN PASSWORD 'pass';
CREATE TABLE private_data (secret TEXT);
-- As readonly_user:
SELECT * FROM private_data; -- triggers 42501Fix 1: Grant the required privilege to the role
When the role should legitimately have access.
-- Grant SELECT on the specific table:
GRANT SELECT ON TABLE private_data TO readonly_user;
-- Grant USAGE on the schema too (required for table access):
GRANT USAGE ON SCHEMA public TO readonly_user;
-- For all future tables in the schema (Postgres 9.0+):
ALTER DEFAULT PRIVILEGES IN SCHEMA public
GRANT SELECT ON TABLES TO readonly_user;Why this works
Postgres evaluates table access by calling pg_class_aclcheck() which checks the ACL (access control list) stored in pg_class.relacl. GRANT adds an entry to this ACL. Schema USAGE is checked separately via pg_namespace.nspacl; both must pass for the query to proceed.
What not to do
Grant SUPERUSER to a role to work around permission errors
Why it's wrong: Superuser bypasses all permission checks including row security; use fine-grained GRANT instead.
Sources
📚 Official docs: https://www.postgresql.org/docs/current/errcodes-appendix.html
📚 Feature docs: https://www.postgresql.org/docs/current/sql-grant.html
🔧 Source ref: src/backend/catalog/aclchk.c — pg_class_aclcheck()
📖 Further reading: GRANT
📖 Further reading: Privileges
Confidence assessment
✅ HIGH confidence
Well-documented and stable. The ACL mechanism has been consistent across all supported versions. Edge case: when using row-level security, users with SELECT privilege may still see 42501 or receive empty results depending on the RLS policy definition.
See also
🔗 Related errors
📄 Reference pages