{"id":"4aea97e5406f62ca","repo":"psycopg/psycopg2","slug":"trying-to-put-unkeyed-connection","errorCode":null,"errorMessage":"trying to put unkeyed connection","messagePattern":"trying to put unkeyed connection","errorType":"exception","errorClass":"PoolError","httpStatus":null,"severity":"error","filePath":"lib/pool.py","lineNumber":103,"sourceCode":"\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:\n            raise PoolError(\"connection pool is closed\")\n\n        if key is None:\n            key = self._rused.get(id(conn))\n            if key is None:\n                raise PoolError(\"trying to put unkeyed connection\")\n\n        if len(self._pool) < self.minconn and not close:\n            # Return the connection into a consistent state before putting\n            # it back into the pool\n            if not conn.closed:\n                status = conn.info.transaction_status\n                if status == _ext.TRANSACTION_STATUS_UNKNOWN:\n                    # server connection lost\n                    conn.close()\n                elif status != _ext.TRANSACTION_STATUS_IDLE:\n                    # connection in error or in transaction\n                    conn.rollback()\n                    self._pool.append(conn)\n                else:\n                    # regular idle connection\n                    self._pool.append(conn)\n            # If the connection is closed, we just discard it.\n        else:","sourceCodeStart":85,"sourceCodeEnd":121,"githubUrl":"https://github.com/psycopg/psycopg2/blob/3a6d9d6ddc6b53eaa80b712f5fa6b23abbdc38db/lib/pool.py#L85-L121","documentation":"Raised by AbstractConnectionPool._putconn() (lib/pool.py:102-103) when putconn() is called with a connection that has no entry in _rused (the id(conn) -> key map). This means the connection was never checked out from this pool (or was already returned), so the pool cannot reconcile its bookkeeping and refuses the return.","triggerScenarios":"Returning a connection obtained elsewhere (not from this pool), returning the same connection twice, or returning a connection after the pool's _used/_rused entries were already cleared (e.g. after closeall cleared state). Calling putconn(conn) without a key relies on _rused lookup at line 101.","commonSituations":"Returning a connection created by psycopg2.connect() directly instead of pool.getconn(). Double-return bugs where a finally block runs twice. Mixing connections between two different pool instances.","solutions":["Only return connections acquired from the same pool via getconn().","Capture and reuse the key returned/used at getconn time, passing it explicitly to putconn(conn, key).","Guard against double-return by setting the connection variable to None after returning it.","Ensure a single pool instance is used; do not mix pool-managed and manually-created connections."],"exampleFix":"// before\nconn = psycopg2.connect(...)\npool.putconn(conn)  # never came from pool\n// after\nconn = pool.getconn()\ntry: use(conn)\nfinally: pool.putconn(conn)","handlingStrategy":"validation","validationCode":"key = pool._rused.get(id(conn))\nif key is None:\n    raise RuntimeError('conn was not checked out from this pool')\npool.putconn(conn, key)","typeGuard":"def conn_belongs_to_pool(pool, conn) -> bool:\n    return id(conn) in pool._rused","tryCatchPattern":"from psycopg2.pool import PoolError\ntry:\n    pool.putconn(conn, key)\nexcept PoolError as e:\n    if 'unkeyed' in str(e):\n        # not from this pool; close it directly\n        try: conn.close()\n        except Exception: pass\n    else: raise","preventionTips":["Capture the key at getconn time and pass it to putconn explicitly.","Set conn = None after putconn to prevent double-return.","Never return connections not acquired from the same pool."],"tags":["pool","bookkeeping","double-return","connection-leak","pool-error"],"analyzedSha":"3a6d9d6ddc6b53eaa80b712f5fa6b23abbdc38db","analyzedAt":"2026-08-04T19:56:51.958Z","schemaVersion":2}