2025, Oct 17 11:00
How to Run Asyncio Code in Jupyter Notebook Without Errors: use top-level await, not asyncio.run
Learn why asyncio.run fails in Jupyter Notebooks (RuntimeError: event loop is closed) and fix it using top-level await and the notebook's existing event loop.
Running asyncio code inside a Jupyter Notebook often trips people up. A common pattern from regular Python scripts, calling asyncio.run(), raises a RuntimeError in notebooks. The root cause is simple, and so is the fix—but you need to use the notebook correctly to avoid fighting its event loop.
Reproducing the issue
The following snippet works in a standalone .py script, but triggers an error in a notebook cell:
import asyncio
async def do_job():
    await asyncio.sleep(1)
    return "Done"
asyncio.run(do_job())
In a Jupyter environment, this tends to fail with:
RuntimeError: Event loop is closed
What’s actually going on
Jupyter already has an event loop running under the hood. asyncio.run() tries to create and manage a fresh event loop from scratch. When a loop is already active, starting a new one isn’t allowed, and that clash results in the error you see. In a regular .py script there’s no loop running until you start one, so asyncio.run() is free to set it up and tear it down without conflict.
The right way to run async code in Jupyter
Don’t start a new loop. Just await the coroutine directly in the cell. Jupyter supports top-level await, so you can call your async functions without wrapping them in asyncio.run().
import asyncio
async def do_job():
    await asyncio.sleep(1)
    return "Done"
await do_job()
This uses the notebook’s existing event loop and avoids the conflict entirely.
Why this matters
Async code behaves differently depending on the runtime environment. In a .py script, asyncio.run() is appropriate because there isn’t an active loop until you create one. In a Jupyter Notebook, a loop is already in place, and attempting to introduce another loop with asyncio.run() will fail. Knowing which environment you’re in prevents confusing errors and helps you write async code that runs predictably.
Conclusion
If you’re in a notebook, rely on top-level await and call your coroutine directly. If you’re in a regular script, use asyncio.run() to manage the event loop. This simple distinction will keep your async workflows smooth across both environments.
The article is based on a question from StackOverflow by Salika Ansari and an answer by Yokozuna.