RuntimeError
asyncio.base_events.RuntimeError: Event loop is closed
Stack trace
Traceback (most recent call last):
File "uvicorn/main.py", line 432, in run
loop.run_until_complete(server.serve())
File "/usr/lib/python3.9/asyncio/base_events.py", line 616, in run_until_complete
self.run_forever()
File "/usr/lib/python3.9/asyncio/base_events.py", line 574, in run_forever
raise RuntimeError('Event loop is closed')
RuntimeError: Event loop is closed Why it happens
This error occurs when the asyncio event loop is closed but code attempts to run asynchronous tasks on it afterward. In FastAPI apps, this often happens if the event loop is closed prematurely by the server or if background tasks try to run after shutdown. Mismanagement of async lifecycle or improper shutdown sequences cause this.
Detection
Monitor logs for 'RuntimeError: Event loop is closed' during app shutdown or background task execution. Use try/except around async calls in shutdown events to catch this early.
Causes & fixes
Uvicorn or ASGI server closes the event loop before all async tasks complete
Ensure all background tasks complete before shutdown by using lifespan events or proper shutdown hooks in FastAPI.
Calling asyncio.run() inside an already running event loop in FastAPI async context
Avoid using asyncio.run() inside FastAPI async functions; instead, use await or create tasks with asyncio.create_task().
Improper use of event loop policy or manual loop closing in code
Do not manually close the event loop; rely on the server's lifecycle management or use context managers properly.
Code: broken vs fixed
import asyncio
from fastapi import FastAPI
app = FastAPI()
@app.get("/")
async def root():
asyncio.run(asyncio.sleep(1)) # This line causes RuntimeError: event loop closed
return {"message": "Hello World"} import os
import asyncio
from fastapi import FastAPI
app = FastAPI()
@app.get("/")
async def root():
await asyncio.sleep(1) # Fixed: use await instead of asyncio.run()
return {"message": "Hello World"}
# Use environment variable for uvicorn command
if __name__ == "__main__":
import uvicorn
uvicorn.run("main:app", host="0.0.0.0", port=8000, reload=True) # Proper server start Workaround
Wrap async calls in try/except RuntimeError and recreate the event loop if closed, but this is fragile and not recommended long-term.
Prevention
Use FastAPI lifespan events to manage startup and shutdown tasks cleanly, avoid manual event loop management, and never call asyncio.run() inside async FastAPI endpoints.