can1357/oh-my-pi · error · RuntimeError

top-level await is not supported from synchronous magic exec

Error message

top-level await is not supported from synchronous magic execution

What it means

_await_sync drains a coroutine with asyncio.run for synchronous magic execution, but if called from inside a running event loop it refuses — asyncio.run cannot nest. It raises RuntimeError: top-level await is not supported from synchronous magic execution.

Source

Thrown at packages/coding-agent/src/eval/py/runner.py:1063

_install_builtins(_STATE.user_ns)


# ---------------------------------------------------------------------------
# Source execution (split last expression for rich display)
# ---------------------------------------------------------------------------


_TLA_FLAG = getattr(ast, "PyCF_ALLOW_TOP_LEVEL_AWAIT", 0x2000)


def _await_sync(coro) -> Any:
    try:
        running_loop = asyncio.get_running_loop()
    except RuntimeError:
        running_loop = None
    if running_loop is not None and running_loop.is_running():
        raise RuntimeError(
            "top-level await is not supported from synchronous magic execution"
        )
    return asyncio.run(coro)


def _run_compiled_sync(code, ns: dict, *, want_value: bool) -> Any:
    """Synchronous execution path used by nested magic helpers."""
    if code.co_flags & inspect.CO_COROUTINE:
        result = _await_sync(eval(code, ns))
        return result if want_value else None
    if want_value:
        return eval(code, ns)
    exec(code, ns)
    return None


async def _run_compiled_async(code, ns: dict, *, want_value: bool) -> Any:
    """Execute a code object in the persistent event loop.

View on GitHub (pinned to 9690622007)

Solutions

  1. Use the async execution path for code containing top-level await
  2. Inside a running loop, await the coroutine directly instead of routing through the sync magic path
  3. Avoid calling sync-only magics from async contexts; rewrite the cell as async
  4. If you have a coroutine in sync code outside a loop, plain asyncio.run works — the error only fires inside a loop

Example fix

// before (inside async cell)
result = %run fetch_data.py  # magic returns coroutine, sync path -> error
// after
result = await run_async_flow()
Defensive patterns

Strategy: try-catch

Validate before calling

try:
    asyncio.get_running_loop()
    in_loop = True
except RuntimeError:
    in_loop = False
# if in_loop, avoid sync magic paths with coroutines; await directly

Try / catch

try:
    result = run_magic_sync(...)
except RuntimeError as e:
    if "top-level await" in str(e):
        result = await run_magic_async(...)  # switch to async path

Prevention

When it happens

Trigger: Calling a magic (or sync-exec path) whose result is a coroutine while already inside a running loop — e.g. invoking %run / a magic from async cell code, or from async magic execution where a sync path is taken with a coroutine argument.

Common situations: Using await from within a sync magic, mixing sync and async exec paths, or an async function's coroutine reaching _run_compiled_sync via a magic.

Related errors


AI-assisted analysis of can1357/oh-my-pi@9690622007 (2026-08-31). Data as JSON: /api/errors/10f7ea50cb85bffc. Report an issue: GitHub.