How to fix 2B000: dependent_privilege_descriptors_still_exist in PostgreSQL

PostgreSQLINTERMEDIATEMEDIUMUpdated Dec 22, 2025ยท 109 views

This error occurs when attempting to drop a PostgreSQL role that still has privileges granted on database objects. PostgreSQL prevents the role from being dropped to maintain security and ownership integrity.

Quick fix

Fixes 2B000: dependent_privilege_descriptors_still_exist

-- Find table/view privileges
SELECT
    schemaname,
    tablename,
    privilege_type
FROM information_schema.table_privileges
WHERE grantee = 'role_name';

-- Find schema privileges
SELECT
    nspname AS schema_name,
    nspacl AS acl
FROM pg_namespace
WHERE nspacl::text LIKE '%role_name%';

-- Find database privileges
SELECT
    datname,
    datacl
FROM pg_database
WHERE datacl::text LIKE '%role_name%';
  1. Identify which privileges are preventing the drop
  2. Use DROP OWNED to remove all privileges
  3. Manually revoke specific privileges (alternative approach)
  4. If the role owns objects, use REASSIGN OWNED first
postgresql โ€” error
-- Find table/view privileges
SELECT
schemaname,
tablename,
privilege_type
FROM information_schema.table_privileges
WHERE grantee = 'role_name';
-- Find schema privileges
SELECT
nspname AS schema_name,
nspacl AS acl
FROM pg_namespace
WHERE nspacl::text LIKE '%role_name%';
-- Find database privileges
SELECT
datname,
datacl
FROM pg_database
WHERE datacl::text LIKE '%role_name%';
2B000: dependent_privilege_descriptors_still_exist

What this error means

The 2B000 error code indicates that dependent privilege descriptors are still present when you attempt to drop a role (user or group). This is PostgreSQL's way of protecting against accidental loss of access control settings. When you grant a role privileges on database objects (tables, schemas, functions, etc.), PostgreSQL creates privilege descriptors that track these permissions. Even if the role doesn't own any objects, it may still have been granted SELECT, INSERT, UPDATE, DELETE, or other privileges on objects owned by other roles. PostgreSQL prevents you from dropping the role until these privilege descriptors are cleaned up. This error is distinct from 2BP01 (dependent_objects_still_exist), which occurs when the role owns objects. Error 2B000 specifically relates to privileges granted to the role on objects it doesn't own.

Typical symptoms

  • DROP ROLE command fails with error code 2B000
  • Error message states "dependent privilege descriptors still exist"
  • Cannot remove a user or role from the database
  • Role removal succeeds in some databases but fails in others within the same cluster

Common causes

  1. The role has been granted privileges (SELECT, INSERT, UPDATE, DELETE, EXECUTE, etc.) on objects owned by other roles
  2. The role has USAGE or other privileges on schemas, sequences, or other database objects
  3. Privileges were granted in multiple databases and not all have been revoked
  4. The role has default privileges configured that haven't been removed
  5. Permissions were granted via GROUP roles and the individual role still inherits those privileges

How to fix "2B000: dependent_privilege_descriptors_still_exist"

1Identify which privileges are preventing the drop

Query the PostgreSQL system catalogs to find what privileges the role has:

sql
-- Find table/view privileges
SELECT
    schemaname,
    tablename,
    privilege_type
FROM information_schema.table_privileges
WHERE grantee = 'role_name';

-- Find schema privileges
SELECT
    nspname AS schema_name,
    nspacl AS acl
FROM pg_namespace
WHERE nspacl::text LIKE '%role_name%';

-- Find database privileges
SELECT
    datname,
    datacl
FROM pg_database
WHERE datacl::text LIKE '%role_name%';

This will show you exactly what permissions need to be removed.

2Use DROP OWNED to remove all privileges

The simplest approach is to use DROP OWNED BY to remove all privileges for the role in the current database:

sql
-- Connect to the database
\c your_database

-- Drop all privileges and owned objects
DROP OWNED BY role_name;

Important: This must be run in EACH database where the role has privileges. REASSIGN OWNED and DROP OWNED cannot access objects in other databases.

sql
-- List all databases
\l

-- For each database:
\c database1
DROP OWNED BY role_name;

\c database2
DROP OWNED BY role_name;

After running DROP OWNED in all databases, you can safely drop the role:

sql
DROP ROLE role_name;
3Manually revoke specific privileges (alternative approach)

If you want more control over which privileges are removed, revoke them explicitly:

sql
-- Revoke table privileges
REVOKE ALL PRIVILEGES ON ALL TABLES IN SCHEMA public FROM role_name;

-- Revoke schema usage
REVOKE USAGE ON SCHEMA public FROM role_name;

-- Revoke database privileges
REVOKE ALL PRIVILEGES ON DATABASE your_database FROM role_name;

-- Revoke sequence privileges
REVOKE ALL PRIVILEGES ON ALL SEQUENCES IN SCHEMA public FROM role_name;

-- Revoke function privileges
REVOKE ALL PRIVILEGES ON ALL FUNCTIONS IN SCHEMA public FROM role_name;

Repeat for each schema and database where the role has privileges, then drop the role:

sql
DROP ROLE role_name;
4If the role owns objects, use REASSIGN OWNED first

If the role also owns objects (causing error 2BP01 in addition to 2B000), you must reassign ownership before dropping privileges:

sql
-- First reassign ownership of all objects
REASSIGN OWNED BY old_role TO new_role;

-- Then drop privileges
DROP OWNED BY old_role;

-- Finally drop the role
DROP ROLE old_role;

The order is critical: REASSIGN OWNED first, then DROP OWNED, then DROP ROLE.

Advanced notes

Multi-database environments: The most common mistake is forgetting that DROP OWNED BY and REASSIGN OWNED only affect the current database. If your PostgreSQL cluster has multiple databases, you must connect to each one and run the cleanup commands separately.

Default privileges: If the role has default privileges configured (ALTER DEFAULT PRIVILEGES), these won't prevent the role from being dropped, but they should be cleaned up for good hygiene:

sql
ALTER DEFAULT PRIVILEGES FOR ROLE old_role REVOKE ALL ON TABLES FROM old_role;

Role memberships: Unlike object ownership and privileges, role memberships (GRANT role_a TO role_b) are automatically cleaned up when you DROP ROLE. You don't need to manually revoke these.

RDS and managed services: On AWS RDS, Azure Database for PostgreSQL, or other managed services, you may need superuser privileges to drop certain roles. If you encounter permission errors, ensure you're connected as the master user or a role with sufficient privileges.

Cascade considerations: While DROP OWNED BY doesn't have a CASCADE option, be aware that it will drop dependent objects like views that depend on tables owned by the role. Always test in a non-production environment first.

Frequently asked questions

What does "2B000: dependent_privilege_descriptors_still_exist" mean?
The 2B000 error code indicates that dependent privilege descriptors are still present when you attempt to drop a role (user or group). This is PostgreSQL's way of protecting against accidental loss of access control settings. ), PostgreSQL creates privilege descriptors that track these permissions.
How do I fix "2B000: dependent_privilege_descriptors_still_exist"?
This error occurs when attempting to drop a PostgreSQL role that still has privileges granted on database objects. PostgreSQL prevents the role from being dropped to maintain security and ownership integrity.
What causes "2B000: dependent_privilege_descriptors_still_exist"?
The role has been granted privileges (SELECT, INSERT, UPDATE, DELETE, EXECUTE, etc.) on objects owned by other roles The role has USAGE or other privileges on schemas, sequences, or other database objects Privileges were granted in multiple databases and not all have been revoked
Is the "2B000: dependent_privilege_descriptors_still_exist" error serious?
It is a medium-severity error โ€” more of a blocker than a risk. The steps above resolve it.

Official resources & further reading