psycopg/psycopg2 · error · PoolError

connection pool is closed

Error message

connection pool is closed

What it means

Raised by AbstractConnectionPool._getconn() (lib/pool.py:78-79) when getconn() is called on a pool whose 'closed' flag is True. closeall() sets closed=True (lib/pool.py:144); after that the pool refuses to hand out connections because all underlying connections have been closed and the pool is decommissioned.

Solutions

  1. Order shutdown so that closeall() is called only after all workers have released their connections.
  2. Guard getconn() callers with a check of pool.closed, or wrap acquisition in try/except psycopg2.pool.PoolError.
  3. In multi-threaded code, use a coordination point (e.g. an event) so threads stop requesting before closeall().
  4. Create a fresh pool for a new lifecycle instead of reusing a closed one.

Example fix

// before
pool.closeall()
conn = pool.getconn()  # shutdown race
// after
# drain workers first, then:
for t in workers: t.join()
pool.closeall()
Defensive patterns

Strategy: validation

Validate before calling

if pool.closed:
    raise RuntimeError('cannot getconn from a closed pool; create a new pool')
conn = pool.getconn()

Type guard

def pool_is_usable(pool) -> bool:
    return not getattr(pool, 'closed', False)

Try / catch

try:
    conn = pool.getconn()
except psycopg2.pool.PoolError as e:
    if 'closed' in str(e):
        pool = create_new_pool(...)
        conn = pool.getconn()
    else: raise

Prevention

When it happens

Trigger: Calling pool.getconn() (or _getconn) after pool.closeall() has been invoked. In threaded pools (ThreadedConnectionPool) this can happen when one thread closes the pool while another tries to acquire a connection.

Common situations: Application shutdown sequences where closeall() runs before all worker threads have finished, or request handlers that lazily grab a connection after a background health-check closed the pool. Also seen in test teardown that closes the pool before fixtures release their connections.

Related errors


AI-assisted analysis of psycopg/psycopg2@3a6d9d6ddc (2026-08-04). Data as JSON: /api/errors/e681cd975acde8c4. Report an issue: GitHub.

Appendix: source

Thrown at lib/pool.py:79

    def _connect(self, key=None):
        """Create a new connection and assign it to 'key' if not None."""
        conn = psycopg2.connect(*self._args, **self._kwargs)
        if key is not None:
            self._used[key] = conn
            self._rused[id(conn)] = key
        else:
            self._pool.append(conn)
        return conn

    def _getkey(self):
        """Return a new unique key."""
        self._keys += 1
        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:

View on GitHub (pinned to 3a6d9d6ddc)