RuntimeError: This event loop is already running / asyncio.run() cannot be called from a running event loop
Jupyter already runs an asyncio event loop, and asyncio.run() refuses to nest inside it. Await directly in the cell — or apply nest_asyncio for libraries that insist.
Updated
The error
RuntimeError: asyncio.run() cannot be called from a running event loop
Older wording of the same collision:
RuntimeError: This event loop is already running
What it means
asyncio runs asynchronous code on an event loop — a scheduler that one thread runs one of. asyncio.run() creates a fresh loop and blocks until done, and it refuses to start inside a thread whose loop is already running. Jupyter's kernel runs on exactly such a loop. So any asyncio.run(...) in a notebook cell — yours, or inside a library you called — collides with the loop that is already spinning.
This bites AI developers constantly: async LLM SDK methods, LangChain and LlamaIndex async APIs, web scrapers and Playwright all reach for the loop.
Why it happens
Code written for scripts uses the standard script idiom (asyncio.run(main())) and gets pasted into a notebook, where the idiom is wrong. Or a library method you called internally does asyncio.run / loop.run_until_complete — correct in scripts, broken under Jupyter — and the traceback points deep inside the library.
How to fix it
1. In a notebook, await directly. Jupyter supports top-level await in cells:
result = await client.messages.create( # no asyncio.run wrapper
model=MODEL, max_tokens=512,
messages=[{"role": "user", "content": "hello"}],
)Anywhere a script would write asyncio.run(coro()), a cell writes await coro(). This is the clean fix for your own code.
2. When a library calls asyncio.run internally, apply nest_asyncio.
pip install nest_asyncioimport nest_asyncio
nest_asyncio.apply()Run once near the top of the notebook. It patches the loop to tolerate nesting, unblocking libraries whose sync wrappers hide async plumbing — the LlamaIndex and scraping-tool cases. Keep it a notebook-only tool; production services deserve fix 3.
3. In scripts and services, structure async properly instead. One asyncio.run(main()) at the entry point, await everywhere inside, and no nested run calls. If sync code must call async code in a running-loop world (an async web framework, for example), that is a design decision — schedule it on the loop (asyncio.create_task, or run_coroutine_threadsafe from another thread) rather than fighting the nesting rule.
4. Use the sync client when you never wanted async. The LLM SDKs offer both: Anthropic()/OpenAI() are synchronous and involve no loop at all. Async earns its complexity for concurrent request fan-out; a sequential notebook rarely needs it.
How to prevent it
Learn the two idioms as a pair: scripts wrap once with asyncio.run; notebooks await at top level. When adopting an async library, check its notebook guidance — mature ones document the nest_asyncio requirement or provide notebook-safe entry points.
Related errors
- The kernel appears to have died — the other class of notebook-specific breakage
- APIConnectionError — async LLM calls' network face
- RateLimitError 429 — what unbounded async fan-out earns you