aio-libs/aiohttp · error · ClientConnectionError
Connector is closed.
Error message
Connector is closed.
What it means
Raised inside BaseConnector.connect() after _create_connection has succeeded, if self._closed became True during the await (another task called connector.close()). The freshly opened proto is closed and the request aborted with ClientConnectionError to avoid handing out a connection from a pool that is being torn down. This is a race between shutdown and a concurrent request.
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 c0ef574e29)
Solutions
- Order shutdown: await all in-flight requests (or cancel and await them) before calling session/connector.close().
- Use a lifespan/guard flag and reject new requests once shutdown has begun, rather than racing close().
- If seen during tests, ensure the test fixture closes the session after the request completes.
Example fix
# before
async def main():
task = asyncio.create_task(session.get(url))
await session.close() # races with task
return await task
# after
async def main():
task = asyncio.create_task(session.get(url))
resp = await task
await session.close()
return resp Defensive patterns
Strategy: validation
Validate before calling
def is_connector_open(connector) -> bool:
return not connector.closed Try / catch
from aiohttp import ClientConnectionError
try:
resp = await session.get(url)
except ClientConnectionError as e:
if 'Connector is closed' in str(e):
# session was shut down concurrently; recreate or give up
...
raise Prevention
- Use an async context manager (`async with aiohttp.ClientSession() as s:`) so lifetime is bounded.
- Await all in-flight tasks before calling close() on the session/connector.
- Track outstanding requests with a counter and gate close() on it reaching zero.
When it happens
Trigger: One coroutine calls `await connector.close()` (or closes the ClientSession that owns the connector) while another is mid-`await session.get(...)` and has just finished establishing the TCP connection.
Common situations: Application shutdown ordering: closing the session while in-flight requests are still settling. Fixtures that close the connector in teardown concurrently with the test making a final call. Cancellation paths where cleanup runs before pending ops drain.
Related errors
- Connector is closed
- Multiple errors on cleanup stage
- Site {site} is not registered in runner {self}
- Cannot write to closing transport
- Session is closed
AI-assisted analysis of aio-libs/aiohttp@c0ef574e29 (2026-08-04).
Data as JSON: /data/errors/bedb2eef88a49a8e.json.
Report an issue: GitHub.