{"id":"e681cd975acde8c4","repo":"psycopg/psycopg2","slug":"connection-pool-is-closed","errorCode":null,"errorMessage":"connection pool is closed","messagePattern":"connection pool is closed","errorType":"exception","errorClass":"PoolError","httpStatus":null,"severity":"error","filePath":"lib/pool.py","lineNumber":79,"sourceCode":"    def _connect(self, key=None):\n        \"\"\"Create a new connection and assign it to 'key' if not None.\"\"\"\n        conn = psycopg2.connect(*self._args, **self._kwargs)\n        if key is not None:\n            self._used[key] = conn\n            self._rused[id(conn)] = key\n        else:\n            self._pool.append(conn)\n        return conn\n\n    def _getkey(self):\n        \"\"\"Return a new unique key.\"\"\"\n        self._keys += 1\n        return self._keys\n\n    def _getconn(self, key=None):\n        \"\"\"Get a free connection and assign it to 'key' if not None.\"\"\"\n        if self.closed:\n            raise PoolError(\"connection pool is closed\")\n        if key is None:\n            key = self._getkey()\n\n        if key in self._used:\n            return self._used[key]\n\n        if self._pool:\n            self._used[key] = conn = self._pool.pop()\n            self._rused[id(conn)] = key\n            return conn\n        else:\n            if len(self._used) == self.maxconn:\n                raise PoolError(\"connection pool exhausted\")\n            return self._connect(key)\n\n    def _putconn(self, conn, key=None, close=False):\n        \"\"\"Put away a connection.\"\"\"\n        if self.closed:","sourceCodeStart":61,"sourceCodeEnd":97,"githubUrl":"https://github.com/psycopg/psycopg2/blob/3a6d9d6ddc6b53eaa80b712f5fa6b23abbdc38db/lib/pool.py#L61-L97","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Order shutdown so that closeall() is called only after all workers have released their connections.","Guard getconn() callers with a check of pool.closed, or wrap acquisition in try/except psycopg2.pool.PoolError.","In multi-threaded code, use a coordination point (e.g. an event) so threads stop requesting before closeall().","Create a fresh pool for a new lifecycle instead of reusing a closed one."],"exampleFix":"// before\npool.closeall()\nconn = pool.getconn()  # shutdown race\n// after\n# drain workers first, then:\nfor t in workers: t.join()\npool.closeall()","handlingStrategy":"validation","validationCode":"if pool.closed:\n    raise RuntimeError('cannot getconn from a closed pool; create a new pool')\nconn = pool.getconn()","typeGuard":"def pool_is_usable(pool) -> bool:\n    return not getattr(pool, 'closed', False)","tryCatchPattern":"try:\n    conn = pool.getconn()\nexcept psycopg2.pool.PoolError as e:\n    if 'closed' in str(e):\n        pool = create_new_pool(...)\n        conn = pool.getconn()\n    else: raise","preventionTips":["Coordinate shutdown: drain workers before closeall().","Check pool.closed before acquiring in long-lived loops."],"tags":["pool","lifecycle","shutdown","threading","pool-error"],"analyzedSha":"3a6d9d6ddc6b53eaa80b712f5fa6b23abbdc38db","analyzedAt":"2026-08-04T19:56:51.958Z","schemaVersion":2}