RuntimeError: This event loop is already running in asyncio
Use await main() directly inside existing async environments (like Jupyter or FastAPI) instead of calling asyncio.run().
Root Cause Analysis
This error occurs when Python tries to initialize and start a new event loop using asyncio.run(), but the current OS thread is already running an active event loop.
1. The Single Loop per Thread Model
Python's asyncio is designed around a single-threaded cooperative multitasking model where exactly one event loop runs per OS thread. When asyncio.run() is called, it creates a new event loop, sets it as the current loop, runs the passed coroutine to completion, and closes the loop. If an event loop is already executing tasks in the thread, starting a second loop is illegal and raises RuntimeError: This event loop is already running.
2. Interactive Environments and Async Frameworks
This error frequently occurs in interactive notebook environments like Jupyter, IPython, or Google Colab, where the notebook kernel itself already runs an asyncio event loop in the main thread. It also occurs in ASGI web servers (such as Uvicorn or Hypercorn) when synchronous route handlers inadvertently call asyncio.run().
3. Best Practices for Event Loop Management
In modern Python 3.10+, application code should define top-level async functions and rely on await rather than managing event loops manually. The standard design rule is to call asyncio.run() exactly once at the absolute entry point of a standalone CLI script.
4. Note de Reproductibilité
Cette erreur survient dans des environnements interactifs (Jupyter Notebook, IPython) ou des serveurs asynchrones déjà actifs. La reproduction exacte dépend de l'état d'exécution de la boucle d'événements globale du thread hôte.
Reproduction Code (MCVE)
import asyncio
async def task():
return 42
async def main():
# Calling asyncio.run() inside a running loop raises RuntimeError
return asyncio.run(task())
asyncio.run(main())
Solution 1: Await Coroutines Directly Without asyncio.run()
When already inside an async coroutine or an active async context, simply await the target coroutine directly instead of trying to launch a new event loop.
import asyncio
async def fetch_data():
await asyncio.sleep(0.01)
return {'status': 'ok', 'data': [1, 2, 3]}
async def main():
# Directly await the coroutine on the active loop
result = await fetch_data()
print('Data fetched successfully:', result)
asyncio.run(main())
Solution 2: Inspect Running Loop State or Use create_task
In dual-mode libraries or notebook scripts, detect whether a loop is already running using asyncio.get_running_loop() to dispatch tasks via create_task.
import asyncio
async def background_worker():
return 'Task complete'
async def entry_point():
try:
loop = asyncio.get_running_loop()
# When running inside a loop, schedule with create_task
task = loop.create_task(background_worker())
result = await task
print('Scheduled via running loop:', result)
except RuntimeError:
# When no loop is running, run standalone
result = asyncio.run(background_worker())
print('Executed via standalone loop:', result)
asyncio.run(entry_point())
A common mistake is attempting to call async functions from synchronous helper functions using asyncio.run() deep inside the call stack. Contrast RuntimeError: This event loop is already running with RuntimeError: no running event loop, which occurs in Python 3.10+ when get_event_loop() is called with no active loop. Never share coroutine objects across multiple OS threads without using asyncio.run_coroutine_threadsafe(). In unit test suites, prefer pytest-asyncio fixtures over manual loop lifecycle management.