PostgreSQL Invalid Grantor (SQLSTATE 0L000)

PostgreSQL raises SQLSTATE 0L000 (invalid_grantor) when a GRANT or REVOKE statement is executed by a role that is not authorized to grant or revoke the specified privilege on the target object. The error message in psql or application logs typically appears as:

ERROR:  grantor must be owner or have WITH GRANT OPTION

or, in contexts involving role membership grants:

ERROR:  must have admin option on role "<role_name>"

What This Error Means

SQLSTATE class 0L is a small SQL-standard class dedicated to grantor-related errors. The only defined condition in this class is invalid_grantor (0L000). PostgreSQL surfaces it when the role executing a GRANT or REVOKE lacks the authority to make that privilege change, either because it does not own the object or because it does not hold the privilege itself with WITH GRANT OPTION (for object privileges) or WITH ADMIN OPTION (for role membership).

The PostgreSQL privilege model distinguishes between holding a privilege and being able to grant it. A role that received SELECT on a table without WITH GRANT OPTION can query the table, but it cannot turn around and grant SELECT to a third role. Only the object owner, a superuser, or a role explicitly granted the privilege WITH GRANT OPTION may issue a downstream GRANT.

The same logic applies to role membership. Granting role app_reader to another role requires that the grantor either is a superuser or holds membership in app_reader with WITH ADMIN OPTION. When neither condition is met, PostgreSQL raises 0L000. Transactions that encounter this error are not aborted — the statement fails, but the session remains open and usable.

Common Causes

  1. Attempting to grant a privilege you hold without WITH GRANT OPTION.
    A role was granted INSERT ON orders but not INSERT ON orders WITH GRANT OPTION. It tries to GRANT INSERT ON orders TO another_role, and PostgreSQL rejects it.

  2. Granting role membership without WITH ADMIN OPTION.
    A role is a member of reporting_role but was not added with WITH ADMIN OPTION. It tries to GRANT reporting_role TO new_user and the error is raised.

  3. Running GRANT as the wrong database user in application deployment scripts.
    A migration or deployment script connects as a limited application user (e.g., app_user) rather than the schema owner or a superuser, then tries to grant privileges on tables it does not own.

  4. Revoking a privilege on behalf of a grantor that is not yourself.
    REVOKE ... GRANTED BY <other_role> requires that the executing role is a superuser or the specified grantor. Running this as an unprivileged role causes 0L000.

How to Fix invalid_grantor

  1. Grant the privilege WITH GRANT OPTION to the issuing role first.
    Before a non-owner role can re-grant a privilege, it must itself hold that privilege with the grant option:

    -- As the table owner or superuser:
    GRANT SELECT ON orders TO reporting_user WITH GRANT OPTION;
    
    -- Now reporting_user can grant to others:
    GRANT SELECT ON orders TO analyst_user;
    
  2. Grant role membership WITH ADMIN OPTION to the issuing role.
    If a role needs to manage membership of another role:

    -- As a superuser or a member of reporting_role WITH ADMIN OPTION:
    GRANT reporting_role TO manager_role WITH ADMIN OPTION;
    
    -- Now manager_role can grant reporting_role to others:
    GRANT reporting_role TO new_employee;
    
  3. Execute the GRANT as the object owner or a superuser.
    The simplest fix for deployment scripts is to connect as the role that owns the schema or tables:

    -- Connect as the schema owner:
    \c mydb schema_owner
    
    GRANT SELECT ON ALL TABLES IN SCHEMA public TO app_readonly;
    

    In psql connection strings or application configs, ensure the deploying user matches the object owner.

  4. Use GRANT ... GRANTED BY syntax carefully (PostgreSQL 16+).
    PostgreSQL 16 introduced GRANTED BY as an explicit clause. Only superusers can specify an arbitrary grantor role. If you are using this syntax, either remove the GRANTED BY clause or run the statement as a superuser:

    -- Requires superuser:
    GRANT SELECT ON orders TO analyst GRANTED BY schema_owner;
    
  5. Check current grant options before attempting downstream grants.
    You can inspect which roles hold grant options via information_schema.role_table_grants or \dp in psql:

    SELECT grantee, privilege_type, is_grantable
    FROM information_schema.role_table_grants
    WHERE table_name = 'orders';
    

Additional Information

  • SQLSTATE class 0L is defined by the SQL standard and contains only 0L000. PostgreSQL does not define any sub-conditions beneath it.
  • The WITH GRANT OPTION mechanism for object privileges and WITH ADMIN OPTION for role membership are conceptually parallel but use different syntax and are stored separately (pg_class.relacl for tables vs. pg_auth_members.admin_option for roles).
  • In PostgreSQL 16, additional role-level membership attributes (SET, INHERIT) were introduced alongside ADMIN OPTION, which may affect which operations require admin option. See PostgreSQL 16 release notes for details.
  • Application frameworks (Rails, Django, Alembic) that run migrations as the application database user rather than the schema owner are a common source of this error in automated deployments. Always verify the migration-time database role.
  • Related SQLSTATE codes in the privilege domain: 42501 (insufficient_privilege) — raised when a role tries to use a privilege it does not hold (as opposed to granting one it cannot).

Frequently Asked Questions

Why does my superuser never see this error?
PostgreSQL superusers bypass all privilege checks, including grantor checks. If you want to reproduce or test this error, you must operate as a non-superuser role.

Can I grant privileges that I own without WITH GRANT OPTION?
Yes — if you own the object (the role is the OWNER of the table, sequence, function, etc.), you can always grant any privilege on it to others, regardless of WITH GRANT OPTION. The grant option only applies to non-owner roles that received the privilege from someone else.

What is the difference between 42501 and 0L000?
42501 (insufficient_privilege) fires when you try to perform an operation (e.g., SELECT, INSERT, EXECUTE) without holding the necessary privilege. 0L000 fires when you try to grant or revoke a privilege and you lack the authority to do so — the error is about the GRANT/REVOKE statement itself, not the underlying object access.

How do I find out who granted a privilege so I can revoke it correctly?
Use the pg_class.relacl array or query information_schema.role_table_grants. The grantor column shows which role issued the grant. You must be that grantor, the object owner, or a superuser to revoke a specific grant.

Subscribe to the Pulse Newsletter

Get early access to new Pulse features, insightful blogs & exclusive events , webinars, and workshops.

We use cookies to provide an optimized user experience and understand our traffic. To learn more, read our use of cookies; otherwise, please choose 'Accept Cookies' to continue using our website.