RuntimeError: Event loop is closed
ID: python/runtimeerror-event-loop-closed
Version Compatibility
| Version | Status | Introduced | Deprecated | Notes |
|---|---|---|---|---|
| 311 | active | — | — | — |
Root Cause
Occurs when asyncio operations are attempted after the event loop has been closed, commonly during cleanup in aiohttp, asyncpg, or when mixing asyncio.run() with manual loop management.
genericWorkarounds
-
92% success Use asyncio.run() as the single entry point and let it manage the loop lifecycle
Replace manual loop.run_until_complete() + loop.close() with a single asyncio.run(main()). This properly handles loop creation, running, and cleanup.
Sources: https://docs.python.org/3/library/asyncio-runner.html
-
90% success For aiohttp, use async with for the ClientSession and ensure cleanup before loop closes
async with aiohttp.ClientSession() as session: async with session.get(url) as resp: return await resp.json() # Session is properly closed before the event loop shuts downSources: https://docs.aiohttp.org/en/stable/client_quickstart.html
-
85% success On Windows, set WindowsSelectorEventLoopPolicy to avoid ProactorEventLoop close issues
import asyncio import sys if sys.platform == 'win32': asyncio.set_event_loop_policy(asyncio.WindowsSelectorEventLoopPolicy())Sources: https://docs.python.org/3/library/asyncio-platforms.html
Dead Ends
Common approaches that don't work:
-
Calling asyncio.get_event_loop() to get a new loop after it was closed
90% fail
get_event_loop() returns the already-closed loop in Python 3.10+. It does not create a new one. The returned loop raises RuntimeError on any operation.
-
Wrapping everything in try/except to suppress the error
75% fail
Suppressing the error masks resource leaks. Connections, file handles, and background tasks won't be cleaned up properly, causing data corruption or resource exhaustion.