diff --git a/packages/code-runtime/code-runtime-python/py/bootstrap.py b/packages/code-runtime/code-runtime-python/py/bootstrap.py index bd61b282e3..d215ff1370 100644 --- a/packages/code-runtime/code-runtime-python/py/bootstrap.py +++ b/packages/code-runtime/code-runtime-python/py/bootstrap.py @@ -877,7 +877,17 @@ async def _pump_replies( ok = bool(frame.get("ok")) value = frame.get("value") message = frame.get("message") - loop.call_soon_threadsafe(complete, fut, ok, value, message) + try: + loop.call_soon_threadsafe(complete, fut, ok, value, message) + except RuntimeError: + # The Future's loop has already closed — the thread that ran + # `asyncio.run(tools.x(...))` finished (its coroutine was cancelled + # or it exited) before this reply arrived, so nothing awaits the + # Future and the reply is moot. Drop it; scheduling onto a closed + # loop raises RuntimeError, and letting that escape would kill the + # pump and strand every later reply — the exact failure class this + # cross-loop delivery exists to prevent. + continue _SCALAR_RE = re.compile(