psycopg/psycopg2 · error · PoolError
connection pool exhausted
Error message
connection pool exhausted
What it means
Raised by AbstractConnectionPool._getconn() (lib/pool.py:91-92) when the pool has no idle connections in _pool and the number of currently checked-out connections (len(_used)) has reached maxconn. The pool cannot create a new connection without exceeding its configured maximum, so it refuses the request rather than silently growing.
Source
Thrown at lib/pool.py:92
return self._keys
def _getconn(self, key=None):
"""Get a free connection and assign it to 'key' if not None."""
if self.closed:
raise PoolError("connection pool is closed")
if key is None:
key = self._getkey()
if key in self._used:
return self._used[key]
if self._pool:
self._used[key] = conn = self._pool.pop()
self._rused[id(conn)] = key
return conn
else:
if len(self._used) == self.maxconn:
raise PoolError("connection pool exhausted")
return self._connect(key)
def _putconn(self, conn, key=None, close=False):
"""Put away a connection."""
if self.closed:
raise PoolError("connection pool is closed")
if key is None:
key = self._rused.get(id(conn))
if key is None:
raise PoolError("trying to put unkeyed connection")
if len(self._pool) < self.minconn and not close:
# Return the connection into a consistent state before putting
# it back into the pool
if not conn.closed:
status = conn.info.transaction_status
if status == _ext.TRANSACTION_STATUS_UNKNOWN:View on GitHub (pinned to 3a6d9d6ddc)
Solutions
- Ensure every getconn() is paired with a putconn() in a try/finally block.
- Raise maxconn to match peak concurrency.
- Audit for connection leaks: log len(pool._used) periodically or use a context manager wrapper.
- Shorten transaction lifetimes so connections return to the pool faster.
- Switch to a pool that blocks/waits (or implement a semaphore) instead of failing fast if bursty traffic is expected.
Example fix
// before
conn = pool.getconn()
use(conn) # exception here leaks the connection
// after
conn = pool.getconn()
try:
use(conn)
finally:
pool.putconn(conn) Defensive patterns
Strategy: try-catch
Validate before calling
if len(pool._used) >= pool.maxconn:
# pool will be exhausted; back off or raise early
raise RuntimeError('pool at maxconn; possible leak') Type guard
def pool_has_capacity(pool) -> bool:
return len(pool._used) < pool.maxconn Try / catch
from psycopg2.pool import PoolError
try:
conn = pool.getconn()
except PoolError as e:
if 'exhausted' in str(e):
# retry after a short delay or reject the request
time.sleep(retry_delay)
conn = pool.getconn()
else: raise Prevention
- Always pair getconn with putconn in try/finally.
- Size maxconn to peak concurrency; monitor len(pool._used).
- Use a context manager that guarantees return.
When it happens
Trigger: Calling getconn() more than maxconn times without returning connections via putconn(). Each getconn() without a matching putconn() adds to _used; when len(_used) == maxconn and _pool is empty, this error fires.
Common situations: Connection leaks — code that forgets to call putconn() (e.g. an exception skips the finally block). Long-running transactions holding connections. Under-provisioned maxconn for the workload concurrency. In threaded pools, more concurrent workers than maxconn.
Related errors
AI-assisted analysis of psycopg/psycopg2@3a6d9d6ddc (2026-08-04).
Data as JSON: /data/errors/1cf9cc85fb62999f.json.
Report an issue: GitHub.