How to Fix 'async event loop error' in AutoGen (Python)

By Cyprian AaronsUpdated 2026-04-21
async-event-loop-errorautogenpython

If you’re seeing async event loop error in AutoGen, it usually means you’re mixing synchronous and asynchronous code in a way that conflicts with Python’s event loop. In practice, this shows up when you call asyncio.run() inside an environment that already has a running loop, or when you use AutoGen async APIs from sync code without awaiting them correctly.

This is common in Jupyter notebooks, FastAPI apps, Streamlit apps, and any service that already owns the loop. The fix is usually not “change AutoGen,” but “stop nesting event loops and use the right entrypoint.”

The Most Common Cause

The #1 cause is calling asyncio.run() from inside an already-running event loop.

AutoGen agents like AssistantAgent and UserProxyAgent often expose async methods such as a_initiate_chat(). If you wrap those in asyncio.run() inside a notebook or async web app, Python raises errors like:

  • RuntimeError: asyncio.run() cannot be called from a running event loop
  • RuntimeError: This event loop is already running

Broken vs fixed pattern

Broken patternFixed pattern
Calls asyncio.run() inside an active loopUses await directly inside async context
Treats async AutoGen APIs like sync functionsKeeps async all the way through
Common in notebooks and ASGI appsWorks in notebooks, FastAPI, etc.
# ❌ Broken: nesting event loops
import asyncio
from autogen import AssistantAgent, UserProxyAgent

assistant = AssistantAgent(name="assistant", llm_config={"config_list": []})
user = UserProxyAgent(name="user", human_input_mode="NEVER")

async def main():
    result = await user.a_initiate_chat(
        assistant,
        message="Write a short summary of ISO 27001."
    )
    print(result)

# In Jupyter / FastAPI / Streamlit this often explodes:
asyncio.run(main())
# ✅ Fixed: await inside an existing async context
from autogen import AssistantAgent, UserProxyAgent

assistant = AssistantAgent(name="assistant", llm_config={"config_list": []})
user = UserProxyAgent(name="user", human_input_mode="NEVER")

async def main():
    result = await user.a_initiate_chat(
        assistant,
        message="Write a short summary of ISO 27001."
    )
    print(result)

# In notebooks:
# await main()

# In FastAPI endpoint:
# return await main()

If you’re in a notebook, use top-level await. If you’re in FastAPI or another async framework, let the framework manage the loop.

Other Possible Causes

1) Calling async AutoGen methods from sync code

If you call a_initiate_chat() without await, you’ll get coroutine-related failures or weird behavior.

# ❌ Broken
result = user.a_initiate_chat(assistant, message="Hello")
print(result)  # coroutine object, not a real result
# ✅ Fixed
result = await user.a_initiate_chat(assistant, message="Hello")

If your function is not async, either make it async or switch to the sync API if AutoGen provides one for your version.

2) Running AutoGen inside FastAPI/Starlette incorrectly

FastAPI endpoints are often already async. Don’t create another loop.

# ❌ Broken
from fastapi import FastAPI
import asyncio

app = FastAPI()

@app.get("/chat")
async def chat():
    return asyncio.run(main())  # RuntimeError: asyncio.run() cannot be called from a running event loop
# ✅ Fixed
@app.get("/chat")
async def chat():
    return await main()

3) Mixing threads and event loops

If you start AutoGen work in a background thread and try to reuse the main thread’s loop, you can hit:

  • RuntimeError: There is no current event loop in thread 'Thread-1'
  • RuntimeError: Event loop is closed
# ❌ Broken idea: reusing the wrong loop across threads
import threading

def worker():
    asyncio.get_event_loop().run_until_complete(main())

threading.Thread(target=worker).start()
# ✅ Fixed: create a fresh loop per thread if you really need threads
def worker():
    asyncio.run(main())

Better yet, avoid threads unless you need them. For agent workflows, keep everything on one async path.

4) Notebook auto-await conflicts with manual loop management

Jupyter already runs an event loop. If you add your own patching or nested runners, things get messy fast.

# ❌ Broken pattern in notebooks
import nest_asyncio
nest_asyncio.apply()
asyncio.run(main())  # still easy to misuse with other libraries

Use:

# ✅ Fixed in notebooks
await main()

If your notebook kernel still complains after that, check whether another library is creating its own loop policy.

How to Debug It

  1. Read the exact traceback

    • If you see asyncio.run() cannot be called from a running event loop, the problem is nested loops.
    • If you see coroutine was never awaited, the problem is missing await.
    • If you see There is no current event loop in thread, the issue is thread context.
  2. Check where your code runs

    • Jupyter notebook?
    • FastAPI/Starlette?
    • Streamlit?
    • Plain Python script?

    That tells you whether you should use top-level await or create your own entrypoint.

  3. Search for these patterns

    • asyncio.run(
    • .a_initiate_chat(
    • .a_*
    • .run_until_complete(

    Any combination of these inside an already-async app is suspicious.

  4. Reduce to a minimal repro Strip everything down to one agent call:

result = await user.a_initiate_chat(assistant, message="ping")

If that works alone but fails in your app, the bug is in how your app manages the event loop.

Prevention

  • Use one model consistently:

    • sync app → sync APIs only
    • async app → async APIs only
  • In notebooks and ASGI apps:

    • prefer top-level await
    • avoid asyncio.run() entirely
  • Wrap AutoGen calls behind one service layer so every agent interaction goes through the same execution path.

If you standardize on that pattern early, this class of error disappears fast.


Keep learning

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

Related Guides