aio-libs/aiohttp · error · RuntimeError
Session and connector have to use same event loop
Error message
Session and connector have to use same event loop
What it means
Raised by ClientSession.__init__ after the connector is captured: connector._loop must be the same running loop as the session's. Connectors cache connections per event loop; cross-loop use would corrupt connection state and break asyncio primitives.
Solutions
- Create both the connector and the session inside the same async function / same loop.
- If you must reuse a connector across requests, do not share it across threads; give each thread its own connector.
- Drop the explicit connector argument and let ClientSession build a fresh TCPConnector on the current loop.
Example fix
// before
connector = TCPConnector() # built outside async / in another loop
async def main():
session = ClientSession(connector=connector)
// after
async def main():
connector = TCPConnector()
session = ClientSession(connector=connector) Defensive patterns
Strategy: validation
Validate before calling
import asyncio
from aiohttp import ClientSession, BaseConnector
def assert_same_loop(connector: BaseConnector) -> None:
running = asyncio.get_running_loop()
if connector._loop is not running:
raise RuntimeError(f'connector loop {connector._loop!r} != running {running!r}') Type guard
import asyncio
def connector_matches_running_loop(connector) -> bool:
return connector._loop is asyncio.get_running_loop() Try / catch
try:
session = ClientSession(connector=connector)
except RuntimeError as e:
if 'same event loop' in str(e):
# rebuild connector on the current loop
connector = TCPConnector()
session = ClientSession(connector=connector)
else:
raise Prevention
- Construct the connector and session in the same async function.
- Never share connectors across threads; create per-worker.
- Avoid module-level connector singletons.
When it happens
Trigger: Constructing a BaseConnector (e.g. TCPConnector) in one thread/loop and passing it to a ClientSession created in another. Creating the connector at module import time (no running loop) and then using it inside an async function in a different loop.
Common situations: Background-thread workers each calling asyncio.run with a shared connector. Test fixtures that build a connector in setUp but create the session in an async test on a fresh loop. Mixing uvloop and the default loop in the same process.
Related errors
- Connector is closed.
- Named Pipes only available in proactor loop under windows
- base_url must have a trailing '/'
- Called while some coroutine is waiting for incoming data.
- Cannot connect to unix socket
AI-assisted analysis of aio-libs/aiohttp@d041d4d0fd (2026-08-11).
Data as JSON: /api/errors/d052c15373419ffc.
Report an issue: GitHub.
Appendix: source
Thrown at aiohttp/client.py:377
DeprecationWarning,
stacklevel=2,
)
if connector is None:
connector = TCPConnector(ssl_shutdown_timeout=ssl_shutdown_timeout)
# Initialize these three attrs before raising any exception,
# they are used in __del__
self._connector = connector
self._loop = loop
if loop.get_debug():
self._source_traceback: traceback.StackSummary | None = (
traceback.extract_stack(sys._getframe(1))
)
else:
self._source_traceback = None
if connector._loop is not loop:
raise RuntimeError("Session and connector have to use same event loop")
if cookie_jar is None:
cookie_jar = CookieJar()
self._cookie_jar = cookie_jar
if cookies:
self._cookie_jar.update_cookies(cookies)
self._connector_owner = connector_owner
self._version = version
self._json_serialize = json_serialize
self._json_serialize_bytes = json_serialize_bytes
self._raise_for_status = raise_for_status
self._auto_decompress = auto_decompress
self._trust_env = trust_env
self._requote_redirect_url = requote_redirect_url
self._read_bufsize = read_bufsize
self._max_line_size = max_line_sizeView on GitHub (pinned to d041d4d0fd)