python/cpython · error · RuntimeError

{self!r} is bound to a different event loop

Error message

{self!r} is bound to a different event loop

What it means

All loop-bound asyncio primitives (Lock, Semaphore, Condition, Event, Queue, Barrier) lazily bind to the first event loop that uses them via _LoopBoundMixin._get_loop(). If the running loop later differs from the bound one, this RuntimeError fires. Since Python 3.10 the binding happens lazily (fixing 'attached to a different loop' at creation), so the error surfaces at first cross-loop use instead.

Source

Thrown at Lib/asyncio/mixins.py:20

import threading
from . import events

_global_lock = threading.Lock()


class _LoopBoundMixin:
    _loop = None

    def _get_loop(self):
        loop = events._get_running_loop()

        if self._loop is None:
            with _global_lock:
                if self._loop is None:
                    self._loop = loop
        if loop is not self._loop:
            raise RuntimeError(f'{self!r} is bound to a different event loop')
        return loop

View on GitHub (pinned to bc6749cc3b)

Solutions

  1. Create loop-bound primitives inside the coroutine/loop that owns them, not at import time
  2. Scope fixtures to the loop lifetime: create the Queue/Lock in an async fixture
  3. For cross-thread sharing, use loop.call_soon_threadsafe / run_coroutine_threadsafe instead of sharing primitives
  4. Restructure to a single long-lived loop (asyncio.run once for the whole app)

Example fix

# before
lock = asyncio.Lock()  # module level
async def main():
    async with lock: ...
asyncio.run(main())
asyncio.run(main())  # second run -> different loop -> RuntimeError

# after
async def main():
    lock = asyncio.Lock()  # created inside the loop that uses it
    async with lock: ...
asyncio.run(main())
Defensive patterns

Strategy: validation

Validate before calling

import asyncio
prim_loop = getattr(prim, '_loop', None)
running = asyncio.events._get_running_loop()
conflict = prim_loop is not None and running is not None and prim_loop is not running

Type guard

def primitive_bound_to_running_loop(prim) -> bool:
    import asyncio
    bound = getattr(prim, '_loop', None)
    running = asyncio.events._get_running_loop()
    return bound is None or running is bound

Try / catch

try:
    async with lock:
        ...
except RuntimeError as e:
    if 'bound to a different event loop' in str(e):
        # recreate the primitive inside this loop and retry once

Prevention

When it happens

Trigger: Creating an asyncio.Lock/Queue at module level and using it under two different asyncio.run() calls; sharing a primitive between the main loop and a loop in another thread; tests that create a new loop per test but share a module-level Queue.

Common situations: Pytest-asyncio with function-scoped loops against module-scoped fixtures holding locks/queues; Jupyter's auto-running loop vs a manual asyncio.run; applications that restart the event loop (watchdogs, hot reload); multi-threaded servers with one loop per thread sharing globals.

Related errors


AI-assisted analysis of python/cpython@bc6749cc3b (2026-08-14). Data as JSON: /api/errors/095aa3b0ac692547. Report an issue: GitHub.