PostgreSQL raises ERROR: invalid grant operation with SQLSTATE 0LP01 and condition name invalid_grant_operation when a GRANT statement attempts an operation that is semantically invalid — most commonly trying to grant a privilege with WITH GRANT OPTION in a context where that option is not permitted, or attempting to grant a privilege that the object type does not support.
What This Error Means
SQLSTATE 0LP01 belongs to PostgreSQL's class 0L — "Invalid Grantor". This class covers errors that arise when a GRANT statement is structurally valid SQL but logically invalid given the privilege model. The error is raised at statement execution time, before any privilege change is committed.
The invalid_grant_operation condition is distinct from insufficient_privilege (SQLSTATE 42501). Insufficient privilege means the grantor lacks the necessary privilege to perform the grant. Invalid grant operation means the grant itself is structurally or semantically disallowed — for example, using WITH GRANT OPTION where the SQL standard or PostgreSQL's implementation prohibits it, or referencing a privilege that does not apply to the target object type.
After this error, the current transaction is not automatically aborted (unlike errors in class XX or constraint violations). However, in autocommit mode the statement simply fails, and in an explicit transaction block you should roll back before retrying.
Common Causes
Using
WITH GRANT OPTIONon a role membership grant. In PostgreSQL,GRANT role TO user WITH GRANT OPTIONis not supported — role membership grants useWITH ADMIN OPTIONinstead. SpecifyingWITH GRANT OPTIONin this context raises0LP01.Granting a privilege that does not apply to the object type. For example, attempting to
GRANT UPDATE ON SEQUENCE seq TO user— sequences supportUSAGE,SELECT, andUPDATE, but some privilege combinations on certain object types are invalid and will trigger this error.Granting
WITH GRANT OPTIONon a privilege that was itself received withoutWITH GRANT OPTION. While this typically raisesinsufficient_privilege, some edge cases in PostgreSQL's privilege propagation logic may surface as0LP01.Driver or ORM-generated GRANT statements with incorrect syntax. Automated migration tools or hand-rolled privilege management scripts sometimes generate
GRANT ... WITH GRANT OPTIONwhenWITH ADMIN OPTIONis intended, particularly when managing role hierarchies.
How to Fix invalid_grant_operation
Replace
WITH GRANT OPTIONwithWITH ADMIN OPTIONfor role grants.-- Incorrect (raises 0LP01): GRANT myrole TO myuser WITH GRANT OPTION; -- Correct for role membership: GRANT myrole TO myuser WITH ADMIN OPTION;WITH ADMIN OPTIONallowsmyuserto in turn grantmyroleto other roles.Use
WITH GRANT OPTIONonly for object privilege grants, not role grants.-- Correct: granting a table privilege with the ability to re-grant it GRANT SELECT ON TABLE mytable TO myuser WITH GRANT OPTION;Verify the privileges applicable to the object type. Check the PostgreSQL documentation for the exact privileges supported by each object type (
TABLE,SEQUENCE,FUNCTION,SCHEMA, etc.) and ensure yourGRANTstatement uses only valid privilege names for that type.-- Check existing privileges on an object SELECT grantee, privilege_type, is_grantable FROM information_schema.role_table_grants WHERE table_name = 'mytable';Inspect the exact statement that failed. If the error comes from a migration tool or ORM, log the generated SQL and check whether it is using
WITH GRANT OPTIONin a role membership context.-- List role memberships and admin options SELECT r.rolname AS role, m.rolname AS member, a.admin_option FROM pg_auth_members a JOIN pg_roles r ON r.oid = a.roleid JOIN pg_roles m ON m.oid = a.member;
Additional Information
- SQLSTATE class
0L("Invalid Grantor") is defined by the SQL standard. PostgreSQL maps0LP01toinvalid_grant_operationinsrc/backend/utils/errcodes.txt. - Related SQLSTATE codes in the privilege/grant space:
42501—insufficient_privilege: the current role does not hold the privilege it is trying to grant.0LP01—invalid_grant_operation: the grant itself is not a legal operation regardless of what privileges the grantor holds.
- PostgreSQL introduced the clear separation between
WITH GRANT OPTION(object privileges) andWITH ADMIN OPTION(role membership) from early versions; this distinction has been stable across all modern PostgreSQL releases (9.x through 17.x). - Most PostgreSQL client drivers (libpq, psycopg, asyncpg, JDBC) surface this as a standard database error with the SQLSTATE attached. In Python's psycopg, it appears as
psycopg.errors.InvalidGrantOperation. - This error has no performance implications — it is a purely semantic check performed during privilege resolution before any I/O occurs.
Frequently Asked Questions
Why does GRANT role TO user WITH GRANT OPTION fail in PostgreSQL?
PostgreSQL uses separate syntax for object-level privileges and role membership. For role membership, the correct option is WITH ADMIN OPTION, not WITH GRANT OPTION. The latter is reserved for granting privileges on database objects such as tables, sequences, and functions. Using WITH GRANT OPTION in a role grant is a SQLSTATE 0LP01 error.
What is the difference between WITH GRANT OPTION and WITH ADMIN OPTION?
WITH GRANT OPTION applies when granting privileges on a specific database object (e.g., GRANT SELECT ON TABLE t TO u WITH GRANT OPTION). It allows the recipient to grant that same object privilege to other roles. WITH ADMIN OPTION applies to role membership grants and allows the recipient to add or remove other members from that role. They are not interchangeable.
Does this error abort my transaction?
No, 0LP01 does not automatically abort the surrounding transaction in PostgreSQL. However, if you are inside an explicit BEGIN block, the safest practice is to issue a ROLLBACK (or ROLLBACK TO SAVEPOINT) before retrying the corrected statement, since some contexts may leave the transaction in an error state depending on how the error is handled by your client.
How do I check which roles have ADMIN OPTION on a given role?
SELECT m.rolname AS member, a.admin_option
FROM pg_auth_members a
JOIN pg_roles r ON r.oid = a.roleid
JOIN pg_roles m ON m.oid = a.member
WHERE r.rolname = 'target_role';
This query shows every role that is a member of target_role and whether they hold ADMIN OPTION.