{"record":{"id":"bedb2eef88a49a8e","repo":"aio-libs/aiohttp","slug":"connector-is-closed","errorCode":null,"errorMessage":"Connector is closed.","messagePattern":"Connector is closed\\.","errorType":"exception","errorClass":"ClientConnectionError","httpStatus":null,"severity":"error","filePath":"aiohttp/connector.py","lineNumber":699,"sourceCode":"\n            try:\n                # Traces are done inside the try block to ensure that the\n                # that the placeholder is still cleaned up if an exception\n                # is raised.\n                if traces:\n                    for trace in traces:\n                        await trace.send_connection_create_start()\n                proto = await self._create_connection(req, traces, timeout)\n                if traces:\n                    for trace in traces:\n                        await trace.send_connection_create_end()\n            except BaseException:\n                self._release_acquired(key, placeholder)\n                raise\n            else:\n                if self._closed:\n                    proto.close()\n                    raise ClientConnectionError(\"Connector is closed.\")\n\n        # The connection was successfully created, drop the placeholder\n        # and add the real connection to the acquired set. There should\n        # be no awaits after the proto is added to the acquired set\n        # to ensure that the connection is not left in the acquired set\n        # on cancellation.\n        self._acquired.remove(placeholder)\n        self._acquired.add(proto)\n        if self._limit_per_host:\n            acquired_per_host = self._acquired_per_host[key]\n            acquired_per_host.remove(placeholder)\n            acquired_per_host.add(proto)\n        return Connection(self, key, proto, self._loop)\n\n    async def _wait_for_available_connection(\n        self, key: \"ConnectionKey\", traces: list[\"Trace\"]\n    ) -> None:\n        \"\"\"Wait for an available connection slot.\"\"\"","sourceCodeStart":681,"sourceCodeEnd":717,"githubUrl":"https://github.com/aio-libs/aiohttp/blob/d041d4d0fd48c3f0832084d33be16cf1c4835f85/aiohttp/connector.py#L681-L717","documentation":"Raised near the end of connect() after a connection was successfully created but the connector was closed in the meantime (self._closed became True during an await). aiohttp closes the freshly created proto and raises ClientConnectionError so the caller does not receive a transport bound to a dead pool. It is a race between concurrent close() and a pending request.","triggerScenarios":"Calling session.close() (which closes the connector) while a request is still connecting; closing a connector shared across tasks before all in-flight requests finish; context-manager exit racing with an in-flight connect.","commonSituations":"Using async with session outside the scope of pending tasks; an error handler that closes the connector aggressively; reconnect logic that recycles connectors too eagerly; tests that tear down the event loop while connections are still being established.","solutions":["Keep the connector/session open until every in-flight request has resolved.","Use a single owned session per long-lived client instead of repeatedly closing and recreating.","Before issuing requests, guard with `if connector.closed: build a new one`.","Drive teardown via asyncio.gather(*pending) then close, so no connect is left mid-flight."],"exampleFix":"# before\nasync with session:\n    task = asyncio.create_task(session.get(url))\nsession.close()  # connector closed while task still connecting\n# after\ntask = asyncio.create_task(session.get(url))\nawait task\nawait session.close()","handlingStrategy":"try-catch","validationCode":"async def request_with_fresh_connector(session_factory, method, url):\n    try:\n        return await session_factory().request(method, url)\n    except aiohttp.ClientConnectionError as exc:\n        if 'Connector is closed' in str(exc):\n            return await session_factory(force_new=True).request(method, url)\n        raise","typeGuard":"null","tryCatchPattern":"try:\n    resp = await session.get(url)\nexcept aiohttp.ClientConnectionError as exc:\n    if 'closed' in str(exc).lower():\n        session = await new_session()  # rebuild connector + session\n        resp = await session.get(url)\n    else:\n        raise","preventionTips":["Own one long-lived session/connector per client and close it only at shutdown.","Await all pending requests before closing the connector.","Guard with `if connector.closed` before reusing a connector that may have been recycled."],"tags":["connector","lifecycle","concurrency","client-connection-error"],"backgroundTag":null,"analyzedSha":"d041d4d0fd48c3f0832084d33be16cf1c4835f85","analyzedAt":"2026-08-11T20:44:15.550Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}