SaaS architecture
database is being accessed by other users in PostgreSQL
PostgreSQL's database is being accessed by other users, measured on 12, 13, 15 and 17: the 5-second wait, WITH (FORCE), its refusals and the reconnect race.
ERROR: database "app" is being accessed by other users (SQLSTATE 55006) means you tried to drop, rename or copy a database while another session was connected to it. PostgreSQL will not remove a database out from under a connection, so the command fails and nothing changes. The DETAIL line says how many sessions are in the way; finding them is a query on pg_stat_activity, and getting rid of them is either DROP DATABASE … WITH (FORCE) or terminating them yourself.
We measured on PostgreSQL 17.11; the plain FORCE drop, the prepared-transaction refusal and the permission refusals also on 13.23, the 5-second wait also on 12.22, 13.23 and 15.19, and CREATE DATABASE … STRATEGY on 15.19, all in containers.
Why does DROP DATABASE say other users are connected?
Because one is — any session, idle or not. An idle psql window was enough:
drop database app;
-- ERROR: 55006: database "app" is being accessed by other users
-- DETAIL: There is 1 other session using the database.
-- (with three sessions: There are 3 other sessions using the database.)
The error is not immediate. DROP DATABASE checks 50 times at 100 ms intervals whether the sessions have gone (CountOtherDBBackends in the PostgreSQL source: “50 tries with 100ms sleep between tries makes 5 sec total wait”); every ‘being accessed’ failure we measured came after 5.04 s (5,035 to 5,041 ms, on 12, 13, 15 and 17). During that wait new connections to the database queue behind it: one we opened 1 s after the drop got in only after 4.04 s, when the drop had failed. In that window it waits for the sessions to leave: with a session that ended its pg_sleep(2) 0.3 s after the drop started, the drop simply succeeded after 1.77 s.
Connected to the database you are dropping, you get a different message straight away: cannot drop the currently open database (also 55006), with or without FORCE. ALTER DATABASE app RENAME TO app2 gave the same “being accessed by other users” error after the same 5.04 s, and from inside the database current database cannot be renamed (0A000). A non-owner got must be owner of database app (42501) before any session check.
The same message comes with a prefix when you copy a database: CREATE DATABASE copy TEMPLATE src with one session on src returned source database "src" is being accessed by other users, also after 5.04 s. A plain CREATE DATABASE copies template1, so a single open session on template1 blocked every CREATE DATABASE on the server with source database "template1" is being accessed by other users; TEMPLATE template0 went through. STRATEGY wal_log and STRATEGY file_copy failed identically on 15 and 17.
Who is connected to the database?
select pid, usename, application_name, client_addr, state, backend_type
from pg_stat_activity
where datname = 'app' and pid <> pg_backend_pid();
-- pid | usename | application_name | client_addr | state | backend_type
-- 1325 | postgres | holder | 172.17.0.1 | active | client backend
-- 1326 | postgres | worker | 172.17.0.1 | idle in transaction | client backend
-- 1327 | postgres | psql | 172.17.0.1 | idle | client backend
All three counted, whatever their state: the drop failed with There are 3 other sessions. An idle connection in an application’s pool, or a database GUI that still has the database open, is a session like any other and shows up as a row here — application_name and client_addr tell you whose. A session idle in transaction holds more than a connection; that is measured in idle in transaction in PostgreSQL.
Not everything in the list blocks. We made autovacuum work slowly on a table in app; pg_stat_activity showed it as backend_type = autovacuum worker running autovacuum: VACUUM ANALYZE public.big, and a plain DROP DATABASE (no FORCE) still succeeded in 0.18 s on 17.11: DROP DATABASE sends autovacuum workers in the database SIGTERM itself before it waits. The server log had FATAL: terminating autovacuum process due to administrator command.
How do you drop a database with active connections?
On PostgreSQL 13 and later: DROP DATABASE app WITH (FORCE). It took 0.17 s on 13.23 and 17.11. A session that was running a query, and an idle one at its next statement, both got:
-- FATAL: 57P01: terminating connection due to administrator command
-- server closed the connection unexpectedly
On 12.22 the option does not exist: ERROR: 42601: syntax error at or near "with".
FORCE does not always work, and when it refuses it terminates no one: in a mixed test (the owner’s own session plus a superuser session) the error came back and both sessions were still connected afterwards.
| What is in the way | Message with WITH (FORCE), PostgreSQL 17 | What to do |
|---|---|---|
| any client session (idle, active, idle in transaction) | none, dropped in 0.17 s; the session gets FATAL 57P01 | — |
| an autovacuum worker | none, also without FORCE | — |
| a prepared transaction | database "app" is being used by prepared transactions / DETAIL: There is 1 prepared transaction using the database. (without FORCE: the usual message after the same 5.04 s, same DETAIL; with FORCE the refusal comes at once, on 13 and 17, and the other session stays connected) | COMMIT PREPARED or ROLLBACK PREPARED from inside the database |
an active logical replication slot (a pg_recvlogical streaming) | database "app" is used by an active logical replication slot / DETAIL: There is 1 active slot., after 35 ms | stop the consumer; an inactive slot was dropped together with the database |
| a superuser’s session, dropped by a non-superuser owner | 42501 permission denied to terminate process / DETAIL: Only roles with the SUPERUSER attribute may terminate processes of roles with the SUPERUSER attribute. (13: must be a superuser to terminate superuser process) | have a superuser drop it |
| another non-superuser role’s session, dropped by the owner | 42501 permission denied to terminate process / DETAIL: Only roles with privileges of the role whose process is being terminated or with privileges of the "pg_signal_backend" role may terminate this process. (13: must be a member of the role whose process is being terminated or member of pg_signal_backend, no DETAIL) | grant pg_signal_backend to the owner: then it worked |
Note the inactive slot: pg_replication_slots went from one slot to zero without a message.
Why does pg_terminate_backend not stop the error?
Because whatever opened the connection opens it again. We ran two clients in a loop that reconnected 50 ms after losing their connection, the way a pool or a worker with retries does, and terminated them with:
select pg_terminate_backend(pid)
from pg_stat_activity
where datname = 'app' and pid <> pg_backend_pid();
| Sequence, two reconnecting clients | Result in 5 runs |
|---|---|
terminate and DROP DATABASE in one psql call | 5 dropped, in 0.08 s |
| terminate, then the drop 0.5 s later | 5 failed after 5.6 s: There are 2 other sessions using the database. |
ALTER DATABASE app ALLOW_CONNECTIONS false, terminate, drop 0.5 s later | 5 dropped |
REVOKE CONNECT ON DATABASE app FROM PUBLIC, terminate, drop 0.5 s later | 5 dropped |
WITH (FORCE) (13+) | 5 dropped, in 0.17 s |
With ALLOW_CONNECTIONS false they got FATAL: database "app" is not currently accepting connections instead, and after REVOKE CONNECT, FATAL: permission denied for database "app". Of the two, ALLOW_CONNECTIONS false is the one that also stopped a superuser; after the REVOKE, our superuser connected without trouble. If you then decide not to drop, set it back to true. WITH (FORCE) did not lose the race in our runs, and that is not luck: DROP DATABASE locks the database before it terminates anyone, so a client that reconnects waits on that lock. But a pool that reconnects to a database that no longer exists just fails differently: FATAL: database "app" does not exist. Stop the application first. Reconnect loops that pile up connections have their own error: remaining connection slots are reserved.
How 55006 relates to the other codes in its class is in our overview of PostgreSQL error codes.
What we did not measure
Subscriptions on the target database, which the documentation also names as a reason for FORCE to refuse. Behaviour behind PgBouncer, where the pooler’s server connections are the sessions you see. How specific frameworks, test runners or GUI tools manage their connections; we only measured psql clients. Streaming (physical) replicas.
Sources: PostgreSQL 17 documentation on DROP DATABASE and template databases, read on 22 October 2026, and the PostgreSQL 17 source (procarray.c, CountOtherDBBackends and TerminateOtherDBBackends). All measurements are our own, on PostgreSQL 17.11, 15.19, 13.23 and 12.22 in containers, on 22 October 2026.