{"id":"95d2e283c018ceee","repo":"aio-libs/aiohttp","slug":"connector-is-closed-95d2e2","errorCode":null,"errorMessage":"Connector is closed","messagePattern":"Connector is closed","errorType":"exception","errorClass":"ClientConnectionError","httpStatus":null,"severity":"error","filePath":"aiohttp/connector.py","lineNumber":1149,"sourceCode":"                raise InvalidUrlClientError(host, \"is not a canonical IPv4 address\")\n            return [\n                {\n                    \"hostname\": host,\n                    \"host\": host,\n                    \"port\": port,\n                    \"family\": self._family,\n                    \"proto\": 0,\n                    \"flags\": 0,\n                }\n            ]\n\n        if not self._use_dns_cache:\n            if traces:\n                for trace in traces:\n                    await trace.send_dns_resolvehost_start(host)\n\n            if self._closed:\n                raise ClientConnectionError(\"Connector is closed\")\n\n            res = await self._resolver.resolve(host, port, family=self._family)\n\n            if traces:\n                for trace in traces:\n                    await trace.send_dns_resolvehost_end(host)\n\n            return res\n\n        key = (host, port)\n        if key in self._cached_hosts and not self._cached_hosts.expired(key):\n            # get result early, before any await (#4014)\n            result = self._cached_hosts.next_addrs(key)\n\n            if traces:\n                for trace in traces:\n                    await trace.send_dns_cache_hit(host)\n            return result","sourceCodeStart":1131,"sourceCodeEnd":1167,"githubUrl":"https://github.com/aio-libs/aiohttp/blob/c0ef574e29109210e96e652771ae4e7b88615fa4/aiohttp/connector.py#L1131-L1167","documentation":"Raised by TCPConnector._resolve_host() when DNS caching is disabled (`use_dns_cache=False`) and the connector is already closed at the moment a fresh resolver call would be made. Distinct from [85] (which fires after the connection is built) - this one fires earlier, before any DNS lookup, when there is no cache to fall back on.","triggerScenarios":"A connector built with `use_dns_cache=False` (or cleared) has had close() called, then a request tries to resolve a host that is not cached.","commonSituations":"Application shutdown racing with in-flight requests on a no-cache connector. Tests that disable DNS caching and tear down the connector between calls. Background tasks outliving the session.","solutions":["Ensure the connector/session lifetime outlives every request that uses it.","Await in-flight requests before calling `await session.close()`.","If you disabled the cache for testing, re-enable it or replace the resolver with a fake so no real lookup is attempted after close."],"exampleFix":"# before\nconnector = aiohttp.TCPConnector(use_dns_cache=False)\nsession = aiohttp.ClientSession(connector=connector)\n# later, after close():\nawait session.get(url)  # -> Connector is closed\n# after\n# keep the session open for the duration of all requests\nasync with session.get(url) as resp:\n    ...\nawait session.close()","handlingStrategy":"validation","validationCode":"def can_resolve(connector) -> bool:\n    return not connector.closed","typeGuard":null,"tryCatchPattern":"from aiohttp import ClientConnectionError\ntry:\n    await session.get(url)\nexcept ClientConnectionError as e:\n    if str(e) == 'Connector is closed':\n        # recreate session/connector\n        ...\n    raise","preventionTips":["Keep the session/connector alive for the full lifetime of all requests when use_dns_cache=False.","In tests, replace the resolver with a stub rather than relying on closing the connector to 'stop' DNS.","Add a shutdown barrier that awaits outstanding requests before close()."],"tags":["connector","lifecycle","dns","shutdown"],"analyzedSha":"c0ef574e29109210e96e652771ae4e7b88615fa4","analyzedAt":"2026-08-04T19:51:05.467Z","schemaVersion":2}