PostgreSQL error 42501
permission denied for table
Your role lacks a privilege the statement needs on the object the message names: a table, a schema, a sequence or a database. Grant that privilege (and USAGE on the schema), or run the statement as a role that has it.
ERROR: permission denied for table orders
Tested on PostgreSQL 18.6, 15.19, 14.24 · Updated 9 October 2026
What it means
PostgreSQL checks privileges object by object. The role you’re connected as (or switched to with
SET ROLE) doesn’t have the privilege this statement needs on the object named in the message, so
the statement didn’t run. The SQLSTATE is 42501 (insufficient_privilege).
The object type tells you what’s missing:
| Message | Usually missing |
|---|---|
permission denied for schema app | USAGE on the schema (to use anything in it), or CREATE (to make new objects there) |
permission denied for table orders | SELECT, INSERT, UPDATE or DELETE on the table |
permission denied for sequence orders_id_seq | USAGE on the sequence behind a serial column |
permission denied for database app | CONNECT (when connecting) or CREATE (for new schemas) |
must be owner of table orders | Ownership: ALTER TABLE, DROP and similar need the owner, not a grant |
To read a table you need both: USAGE on its schema and SELECT on the table. The schema is checked
first, so you can fix one error and meet the next.
Common causes
- Nothing was granted to a new role. Creating a role gives it no access to existing tables.
- The table is newer than the grant.
GRANT … ON ALL TABLES IN SCHEMAcovers the tables that exist when you run it, not ones created later. USAGEon the schema is missing, so table grants don’t help yet.- A
serialcolumn. Inserting callsnextval()on its sequence, which needs its own grant. Identity columns (GENERATED … AS IDENTITY) don’t. - PostgreSQL 15 or later, and schema
public. Ordinary roles can no longer create tables inpublicby default. - The statement changes structure. Migrations run as an app role that isn’t the table’s owner
fail with
must be owner of table.
How to fix it
See what the role has
SELECT has_schema_privilege('<role>', 'app', 'USAGE') AS schema_usage,
has_table_privilege('<role>', 'app.orders', 'SELECT') AS can_select,
has_table_privilege('<role>', 'app.orders', 'UPDATE') AS can_update;
In psql, \dp app.orders lists a table’s grants. In its output, r is SELECT, a is
INSERT, w is UPDATE and d is DELETE.
Grant what’s missing
As the owner or a superuser:
GRANT USAGE ON SCHEMA app TO <role>;
GRANT SELECT, INSERT, UPDATE, DELETE ON ALL TABLES IN SCHEMA app TO <role>;
GRANT USAGE ON ALL SEQUENCES IN SCHEMA app TO <role>;
Grant only what the role needs: a reporting role needs SELECT, not the rest.
Cover tables created later
ALTER DEFAULT PRIVILEGES FOR ROLE <owner> IN SCHEMA app
GRANT SELECT, INSERT, UPDATE, DELETE ON TABLES TO <role>;
ALTER DEFAULT PRIVILEGES FOR ROLE <owner> IN SCHEMA app
GRANT USAGE ON SEQUENCES TO <role>;
Default privileges apply to objects created by <owner> (the role your migrations run as), from
now on. They don’t change existing tables, and tables created by a different role don’t get them.
For read-only access to everything, PostgreSQL 14 and later have a built-in role:
GRANT pg_read_all_data TO <role>;
It acts like SELECT on every table and USAGE on every schema, but doesn’t bypass row-level
security.
Schema public in PostgreSQL 15 and later
PostgreSQL 15 removed the CREATE privilege on schema public from everyone (PUBLIC), and made
the database’s owner (through the pg_database_owner role) the owner of public. An app role that
could create its tables in public on PostgreSQL 14 gets permission denied for schema public on a
new PostgreSQL 15 database. The change applies to new clusters and new databases; a cluster upgraded
with pg_upgrade, or a restored dump, keeps the old permissions.
Pick one fix:
-- Give the app its own schema (the cleanest).
CREATE SCHEMA app AUTHORIZATION <role>;
-- Or make the app role the database owner, which makes it owner of public.
ALTER DATABASE <database> OWNER TO <role>;
-- Or grant the old behaviour back for this role.
GRANT CREATE ON SCHEMA public TO <role>;
With its own schema, put it first on the role’s search path:
ALTER ROLE <role> SET search_path = app, public;.
“must be owner of table”
Run migrations as the role that owns the tables, or transfer ownership:
ALTER TABLE app.orders OWNER TO <migration role>;
Reproduce it
On PostgreSQL 18.6, as a superuser: a table with a serial column, and a new role with nothing
granted. SET ROLE switches to that role. With \set VERBOSITY verbose (LOCATION lines trimmed):
SET ROLE seo_pgconn_app;
SELECT * FROM seo_pgconn.orders;
ERROR: 42501: permission denied for schema seo_pgconn
LINE 1: SELECT * FROM seo_pgconn.orders;
^
After GRANT USAGE ON SCHEMA seo_pgconn TO seo_pgconn_app:
ERROR: 42501: permission denied for table orders
After GRANT SELECT, INSERT ON seo_pgconn.orders TO seo_pgconn_app, an insert needs the sequence:
INSERT INTO seo_pgconn.orders (total) VALUES (10);
ERROR: 42501: permission denied for sequence orders_id_seq
After GRANT USAGE ON SEQUENCE seo_pgconn.orders_id_seq, the insert works; an update and a schema
change still fail:
UPDATE seo_pgconn.orders SET total = 11;
ERROR: 42501: permission denied for table orders
ALTER TABLE seo_pgconn.orders ADD COLUMN note text;
ERROR: 42501: must be owner of table orders
GRANT SELECT ON ALL TABLES IN SCHEMA seo_pgconn didn’t cover a table created a moment later
(permission denied for table refunds); after ALTER DEFAULT PRIVILEGES IN SCHEMA seo_pgconn GRANT SELECT ON TABLES TO seo_pgconn_app, the next new table was readable.
The PostgreSQL 15 change. In a new database on each server, as a role with no grants:
SET ROLE seo_pgconn_app;
CREATE TABLE public.seo_t (id int);
PostgreSQL 14.24 creates the table. PostgreSQL 15.19 and 18.6 refuse:
ERROR: permission denied for schema public
LINE 1: CREATE TABLE public.seo_t (id int);
^
The schema’s owner and privileges show the difference:
-- PostgreSQL 14.24
nspname | owner | nspacl
---------+-------+----------------------------
public | inlet | {inlet=UC/inlet,=UC/inlet}
-- PostgreSQL 15.19
nspname | owner | nspacl
---------+-------------------+---------------------------------------------------------------
public | pg_database_owner | {pg_database_owner=UC/pg_database_owner,=U/pg_database_owner}
=UC means everyone (PUBLIC) has USAGE and CREATE; =U means USAGE only. On 15.19, both
GRANT CREATE ON SCHEMA public TO seo_pgconn_app and making that role the database’s owner let it
create the table.
In Inlet
Inlet’s roles and grants view shows what each role can do, so you can see which privilege is missing.
In the query editor, the error appears at the position the server reports, and Ask Claude (with your
own Anthropic API key) can explain it and write the GRANT you need.
Related
Sources
- www.postgresql.org/docs/current/ddl-priv.html
- www.postgresql.org/docs/current/sql-grant.html
- www.postgresql.org/docs/current/sql-alterdefaultprivileges.html
- www.postgresql.org/docs/current/ddl-schemas.html#DDL-SCHEMAS-PUBLIC
- www.postgresql.org/docs/release/15.0/
- www.postgresql.org/docs/current/predefined-roles.html
- www.postgresql.org/docs/current/errcodes-appendix.html