- SQLSTATE
- 42501
- Condition name
insufficient_privilege- Product
- PostgreSQL
- Versions
- PostgreSQL 13 to 18; no CREATE on schema public for ordinary roles by default from PostgreSQL 15 (examples run on PostgreSQL 18.6 in PGlite)
- Last reviewed
- 24 October 2026
On this page
What it means
SQLSTATE 42501 means the current role lacks a privilege the statement needs: on the table, its schema, a sequence or function, or ownership of the object.
PostgreSQL checks privileges on every object a statement touches. Reading a table needs SELECT on it, and also USAGE on its schema; inserting into a table with a serial column needs INSERT on the table and USAGE on the sequence behind the column; calling a function needs EXECUTE; and ALTER TABLE, DROP and similar commands need ownership of the object. When any check fails, the statement raises 42501 (insufficient_privilege) and the message names the object type and the object, which tells you exactly which GRANT is missing. Column privileges, row-level security policies and COPY to or from server files are checked under the same SQLSTATE.
Two version changes catch people out. From PostgreSQL 15, new databases no longer let every role create objects in the public schema: only the database owner (and roles it grants CREATE to) can, so CREATE TABLE as an ordinary role fails with "permission denied for schema public". And privileges apply to objects that exist when you grant them, so tables created later are not covered unless default privileges were set. A role that lacks CONNECT on a database is refused at login with the same SQLSTATE; that needs a real server login and is not reproduced on this page.
Common causes
- The application role was never granted SELECT, INSERT, UPDATE or DELETE on the table, or the table was created after the grants were made.
- The role has privileges on the table but not USAGE on its schema.
- INSERT into a table with a serial column: the role has INSERT on the table but not USAGE on its sequence.
- A migration or ad hoc ALTER TABLE runs as the application role, which does not own the table.
- On PostgreSQL 15 or later, an ordinary role tries to CREATE TABLE in schema public of a new database.
- A row-level security policy rejects the row, or COPY reads a server file without membership of pg_read_server_files.
Diagnose it
Read-only These queries only read the system catalogs (pg_catalog, information_schema) and statistics views such as pg_stat_activity and pg_locks. They do not change data, roles or settings, so they are safe to run first.
Which role is running the statement?
SELECT current_user, session_user;
SELECT r.rolname AS member_of
FROM pg_roles AS r
WHERE pg_has_role(current_user, r.oid, 'MEMBER')
ORDER BY 1; current_user is the role whose privileges are checked (it differs from session_user after SET ROLE or inside SECURITY DEFINER functions), followed by every role it is a member of. Privileges granted to those group roles count too.
Check each privilege the statement needs
SELECT has_schema_privilege('app_user', 'public', 'USAGE') AS schema_usage,
has_table_privilege('app_user', 'invoices', 'SELECT') AS can_select,
has_table_privilege('app_user', 'invoices', 'INSERT') AS can_insert,
has_sequence_privilege('app_user', 'invoices_id_seq', 'USAGE') AS sequence_usage; true or false for each privilege. The false ones are the GRANTs to add. Run it as an administrator, naming the application role.
Who owns the table, and what has been granted?
SELECT tableowner FROM pg_tables WHERE tablename = 'invoices';
SELECT grantee, privilege_type
FROM information_schema.role_table_grants
WHERE table_name = 'invoices'
ORDER BY grantee, privilege_type; The owner (needed for ALTER and DROP) and every role with privileges on the table. psql's \dp invoices shows the same access list.
Is row-level security enabled, and with which policies?
SELECT relname, relrowsecurity, relforcerowsecurity
FROM pg_class
WHERE relname = 'notes';
SELECT policyname, cmd, roles, qual, with_check
FROM pg_policies
WHERE tablename = 'notes'; Whether RLS is on for the table and the conditions each policy applies. A row that fails a policy's WITH CHECK condition is rejected with "new row violates row-level security policy".
How to fix it
Each fix is labelled with what it changes. Try read-only checks and the least invasive fix first, and test changes outside production.
Grant only the privileges the role needs
Changes permissionsRun as the table owner or an administrator. Grant the specific privileges the application uses, preferably to a group role that the login roles are members of, instead of to each user. Never make the application role a superuser or grant it ALL to make the error go away.
GRANT USAGE ON SCHEMA public TO app_rw;
GRANT SELECT, INSERT, UPDATE ON invoices TO app_rw;
GRANT app_rw TO app_user;
Grant USAGE on the sequence, or use an identity column
Changes permissionsA serial column takes its values from a separate sequence, which needs its own grant. An identity column (GENERATED ALWAYS AS IDENTITY) does not: INSERT on the table is enough.
GRANT USAGE ON SEQUENCE invoices_id_seq TO app_rw;
Cover tables created later with default privileges
Changes permissionsDefault privileges apply to objects created afterwards by the role that runs the command (or the role named in FOR ROLE), so run it as the role that creates tables, usually the migration owner.
ALTER DEFAULT PRIVILEGES IN SCHEMA public
GRANT SELECT, INSERT, UPDATE, DELETE ON TABLES TO app_rw;
ALTER DEFAULT PRIVILEGES IN SCHEMA public
GRANT USAGE ON SEQUENCES TO app_rw;
Run schema changes as the owner, in a schema it owns
Changes schemaFor "must be owner" and "permission denied for schema public" (PostgreSQL 15 and later), let a dedicated owner role create and alter the application's objects, for example in its own schema, and keep the application role limited to data access. Granting CREATE on public to every role restores the old behaviour but weakens security.
CREATE SCHEMA app AUTHORIZATION app_owner;
Examples
Reading a table without SELECT
CREATE ROLE app_user NOLOGIN;
CREATE TABLE invoices (id serial PRIMARY KEY, amount numeric(10,2));
SET ROLE app_user;
SELECT * FROM invoices;
ERROR: permission denied for table invoices
Reproduced on PGlite 0.5.11 (PostgreSQL 18.6 compiled to WebAssembly), 24 October 2026.
INSERT into a serial table without the sequence grant
With an identity column instead of serial, the same INSERT succeeds without a sequence grant (checked in the same way).
CREATE ROLE app_user NOLOGIN;
CREATE TABLE invoices (id serial PRIMARY KEY, amount numeric(10,2));
GRANT SELECT, INSERT ON invoices TO app_user;
SET ROLE app_user;
INSERT INTO invoices (amount) VALUES (5);
ERROR: permission denied for sequence invoices_id_seq
Reproduced on PGlite 0.5.11 (PostgreSQL 18.6 compiled to WebAssembly), 24 October 2026.
Table privileges without USAGE on the schema
CREATE ROLE app_user NOLOGIN;
CREATE SCHEMA reports;
CREATE TABLE reports.monthly (m int);
GRANT SELECT ON reports.monthly TO app_user;
SET ROLE app_user;
SELECT * FROM reports.monthly;
ERROR: permission denied for schema reports
Reproduced on PGlite 0.5.11 (PostgreSQL 18.6 compiled to WebAssembly), 24 October 2026.
CREATE TABLE in schema public as an ordinary role
This is the default from PostgreSQL 15 in new databases. Databases upgraded or restored from older versions keep their existing permissions on public, so the same statement may still work there.
CREATE ROLE app_user NOLOGIN;
SET ROLE app_user;
CREATE TABLE scratch (x int);
ERROR: permission denied for schema public
Reproduced on PGlite 0.5.11 (PostgreSQL 18.6 compiled to WebAssembly), 24 October 2026.
How to prevent it
Design roles up front: an owner role that runs migrations, a read-write group role and a read-only group role for the application, with login roles as members. Grant schema USAGE, table privileges and sequence USAGE to the group roles, and set ALTER DEFAULT PRIVILEGES so new tables are covered automatically.
Prefer identity columns over serial, keep the application role out of DDL, and test deployments with the real application role rather than an administrator account, so missing grants appear before production.
Common questions
How do I fix "permission denied for table" in PostgreSQL?
As the table owner or an administrator, grant the role the specific privilege it needs, for example GRANT SELECT, INSERT ON invoices TO app_rw, and check that it also has USAGE on the schema (and on the sequence for serial columns). Do not use superuser or GRANT ALL as a shortcut.
Why do I get "permission denied for schema public" on a new database?
Since PostgreSQL 15, ordinary roles cannot create objects in schema public of a newly created database by default. Create the objects as the database owner or a dedicated owner role, or give that role its own schema.
I granted SELECT on all tables, so why is a new table denied?
GRANT ... ON ALL TABLES IN SCHEMA covers only tables that exist at that moment. Use ALTER DEFAULT PRIVILEGES, run by the role that creates the tables, so future tables get the same grants.
What does "must be owner of table" mean?
ALTER TABLE, DROP TABLE, CREATE INDEX and similar commands can be run only by the table's owner (or a member of the owning role). Run them as the owner role instead of the application role.
Learn more on CodeWithSQL
Official documentation
- PostgreSQL error codes (Appendix A)
- Privileges
- GRANT
- PostgreSQL 15 release notes (public schema permissions)
Last technically reviewed 24 October 2026; examples and diagnostics run on PostgreSQL 18.6 (PGlite 0.5.11). SQL-level examples were run in PGlite (PostgreSQL compiled to WebAssembly); examples that need a running server are shown as documented in the PostgreSQL documentation.