aio-libs/aiohttp · error · ClientConnectionError
Connector is closed.
Error message
Connector is closed.
What it means
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.
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.
Example fix
# before
async with session:
task = asyncio.create_task(session.get(url))
session.close() # connector closed while task still connecting
# after
task = asyncio.create_task(session.get(url))
await task
await session.close() Defensive patterns
Strategy: try-catch
Validate before calling
async def request_with_fresh_connector(session_factory, method, url):
try:
return await session_factory().request(method, url)
except aiohttp.ClientConnectionError as exc:
if 'Connector is closed' in str(exc):
return await session_factory(force_new=True).request(method, url)
raise Type guard
null
Try / catch
try:
resp = await session.get(url)
except aiohttp.ClientConnectionError as exc:
if 'closed' in str(exc).lower():
session = await new_session() # rebuild connector + session
resp = await session.get(url)
else:
raise Prevention
- 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.
When it happens
Trigger: 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.
Common situations: 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.
Related errors
- Connector is closed
- Session and connector have to use same event loop
- Already started
- Call .prepare() first
- Call runner.setup() before making a site
AI-assisted analysis of aio-libs/aiohttp@d041d4d0fd (2026-08-11).
Data as JSON: /api/errors/bedb2eef88a49a8e.
Report an issue: GitHub.
Appendix: source
Thrown at aiohttp/connector.py:699
try:
# Traces are done inside the try block to ensure that the
# that the placeholder is still cleaned up if an exception
# is raised.
if traces:
for trace in traces:
await trace.send_connection_create_start()
proto = await self._create_connection(req, traces, timeout)
if traces:
for trace in traces:
await trace.send_connection_create_end()
except BaseException:
self._release_acquired(key, placeholder)
raise
else:
if self._closed:
proto.close()
raise ClientConnectionError("Connector is closed.")
# The connection was successfully created, drop the placeholder
# and add the real connection to the acquired set. There should
# be no awaits after the proto is added to the acquired set
# to ensure that the connection is not left in the acquired set
# on cancellation.
self._acquired.remove(placeholder)
self._acquired.add(proto)
if self._limit_per_host:
acquired_per_host = self._acquired_per_host[key]
acquired_per_host.remove(placeholder)
acquired_per_host.add(proto)
return Connection(self, key, proto, self._loop)
async def _wait_for_available_connection(
self, key: "ConnectionKey", traces: list["Trace"]
) -> None:
"""Wait for an available connection slot."""View on GitHub (pinned to d041d4d0fd)