How to Fix 'async event loop error' in LangChain (Python)
An async event loop error in LangChain usually means you tried to run async code from the wrong place, or you mixed sync and async calls in a way Python’s event loop does not allow. It shows up a lot in notebooks, FastAPI handlers, Streamlit apps, and scripts that call asyncio.run() inside an already running loop.
The exact message varies, but the common ones look like:
- •
RuntimeError: asyncio.run() cannot be called from a running event loop - •
RuntimeError: This event loop is already running - •
RuntimeError: Task got Future attached to a different loop
The Most Common Cause
The #1 cause is calling asyncio.run() inside an environment that already has an active event loop.
That happens often with LangChain async methods like ainvoke(), abatch(), or async retrievers when you wrap them incorrectly.
Broken vs fixed
| Broken pattern | Right pattern |
|---|---|
Calling asyncio.run() inside Jupyter, FastAPI, or another async function | Use await inside async code, or create one top-level async entrypoint |
Mixing sync .invoke() with async .ainvoke() in the same flow without control | Keep the whole path sync or async |
# BROKEN
import asyncio
from langchain_openai import ChatOpenAI
llm = ChatOpenAI(model="gpt-4o-mini")
async def generate():
return await llm.ainvoke("Write a short summary.")
# In Jupyter / FastAPI / any running loop:
result = asyncio.run(generate()) # RuntimeError: asyncio.run() cannot be called from a running event loop
print(result)
# FIXED
from langchain_openai import ChatOpenAI
llm = ChatOpenAI(model="gpt-4o-mini")
async def generate():
return await llm.ainvoke("Write a short summary.")
# In an async context:
result = await generate()
print(result)
If you are in a plain Python script, this is fine:
import asyncio
if __name__ == "__main__":
print(asyncio.run(generate()))
But if you are already inside an async framework, do not nest event loops.
Other Possible Causes
1) Calling sync LangChain methods from inside async code
This is common when someone writes an async API route but uses sync-only calls inconsistently.
# BROKEN
@app.get("/chat")
async def chat():
response = llm.invoke("Hello") # sync call inside async handler
return {"text": response.content}
# FIXED
@app.get("/chat")
async def chat():
response = await llm.ainvoke("Hello")
return {"text": response.content}
If your chain supports async, use the async version end-to-end.
2) Using different loops across threads
You can hit:
- •
RuntimeError: Task got Future attached to a different loop - •
RuntimeError: There is no current event loop in thread
This often happens when LangChain objects are created in one thread and used in another.
# BROKEN
import threading
def worker(chain):
# chain was created on the main thread/event loop
print(chain.invoke("test"))
threading.Thread(target=worker, args=(chain,)).start()
Fix it by creating and using async resources in the same thread/loop, or by handing off work through proper queues/executors.
# FIXED: keep execution on one loop/thread
async def worker():
result = await chain.ainvoke("test")
print(result)
3) Notebook-specific event loop behavior
Jupyter and IPython already run an event loop. That means this breaks:
# BROKEN in notebooks
import asyncio
await asyncio.run(generate())
Use direct await instead:
# FIXED in notebooks
result = await generate()
result
If some third-party code forces asyncio.run(), that code needs to be refactored. Do not patch around it unless you understand the side effects.
4) Mixing incompatible LangChain components
Some integrations expose both sync and async APIs, but not all pieces are fully aligned. A retriever, tool, or callback handler may be synchronous while the rest of the chain is async.
Example symptoms:
- •Chain hangs before first token
- •Callback errors during streaming
- •Event-loop errors only when tools are enabled
A safer pattern is to keep all components on one path:
# GOOD: fully async pipeline
docs = await retriever.ainvoke("aml policy")
response = await chain.ainvoke({"context": docs, "question": "What applies?"})
If one component only has sync methods, isolate it outside the async request path.
How to Debug It
- •
Read the exact exception text
- •If you see
asyncio.run() cannot be called from a running event loop, you are nesting loops. - •If you see
Task got Future attached to a different loop, you have cross-loop/thread usage. - •If you see
There is no current event loop in thread, your code is running outside the main asyncio context.
- •If you see
- •
Search for every
asyncio.run()- •In notebooks and web apps, this is usually the first thing to remove.
- •Replace it with
awaitif you are already inside an async function.
- •
Check whether each LangChain call is sync or async
- •Sync:
.invoke(),.batch() - •Async:
.ainvoke(),.abatch() - •Do not mix them casually in one request flow.
- •Sync:
- •
Print where your code is executing
import asyncio, threading print("thread:", threading.current_thread().name) try: print("running_loop:", asyncio.get_running_loop()) except RuntimeError as e: print("no running loop:", e)If there is already a running loop, stop using
asyncio.run()there.
Prevention
- •
Pick one execution model per app path:
- •pure sync for scripts and cron jobs
- •pure async for FastAPI, WebSocket handlers, notebook workflows
- •
Standardize on LangChain’s matching method names:
- •
.invoke()with sync code - •
.ainvoke()with async code
- •
- •
Add a small integration test for your runtime target:
- •Jupyter notebook cell execution
- •FastAPI endpoint
- •CLI script
This catches event-loop mistakes before production does.
If you want the shortest fix: stop nesting asyncio.run(), switch to await inside existing async contexts, and make sure every LangChain component follows the same sync/async path.
Keep learning
- •The complete AI Agents Roadmap — my full 8-step breakdown
- •Free: The AI Agent Starter Kit — PDF checklist + starter code
- •Work with me — I build AI for banks and insurance companies
By Cyprian Aarons, AI Consultant at Topiax.
Want the complete 8-step roadmap?
Grab the free AI Agent Starter Kit — architecture templates, compliance checklists, and a 7-email deep-dive course.
Get the Starter Kit