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

  1. Create both the connector and the session inside the same async function / same loop.
  2. If you must reuse a connector across requests, do not share it across threads; give each thread its own connector.
  3. 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

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


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_size

View on GitHub (pinned to d041d4d0fd)