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

By Cyprian AaronsUpdated 2026-04-21
async-event-loop-error-in-productioncrewaipython

An async event loop error in production with CrewAI usually means Python tried to run an async workflow while an event loop was already active, closed, or being managed by another framework. You’ll typically hit it when calling crew.kickoff() from FastAPI, Jupyter, Celery, or any app that already owns the loop.

In practice, the failure shows up as one of these patterns:

  • RuntimeError: This event loop is already running
  • RuntimeError: There is no current event loop in thread 'MainThread'
  • RuntimeError: Event loop is closed

The Most Common Cause

The #1 cause is mixing sync and async incorrectly.

CrewAI gives you both sync and async entry points depending on how you structure your app. The broken pattern is usually calling a blocking CrewAI kickoff inside an async function, or trying to manually manage the loop with asyncio.run() where a loop already exists.

Broken vs fixed

Broken patternFixed pattern
Calling sync code inside an async routeAwaiting the async API properly
Wrapping asyncio.run() inside a running loopUsing the framework’s async context
Letting CrewAI create/manage loops indirectlyManaging one loop at the app boundary
# ❌ Broken: FastAPI route + blocking kickoff + nested loop risk
from fastapi import FastAPI
from crewai import Agent, Task, Crew
import asyncio

app = FastAPI()

@app.get("/run")
async def run_crew():
    crew = Crew(
        agents=[Agent(role="Researcher", goal="Find facts", backstory="...")],
        tasks=[Task(description="Research the topic", expected_output="Summary")]
    )

    # Common mistake: forcing asyncio.run inside an already-running server loop
    result = asyncio.run(crew.kickoff())
    return {"result": result}
# ✅ Fixed: keep the route async and use the correct execution style
from fastapi import FastAPI
from crewai import Agent, Task, Crew

app = FastAPI()

@app.get("/run")
async def run_crew():
    crew = Crew(
        agents=[Agent(role="Researcher", goal="Find facts", backstory="...")],
        tasks=[Task(description="Research the topic", expected_output="Summary")]
    )

    # Use the API that matches your CrewAI version and runtime model
    result = await crew.kickoff_async()
    return {"result": result}

If your installed CrewAI version doesn’t expose kickoff_async(), then don’t force async at the route level. Move the work into a sync worker thread or a background job queue.

Other Possible Causes

1) Running CrewAI in Jupyter or IPython with asyncio.run()

Notebook environments already have an event loop running.

# ❌ Broken
import asyncio

asyncio.run(crew.kickoff())
# ✅ Fixed
result = await crew.kickoff_async()

If you must stay in a notebook, use top-level await instead of asyncio.run().

2) Using Celery workers with an async task body

Celery tasks are usually sync unless you explicitly design for async execution.

# ❌ Broken
from celery import shared_task

@shared_task
def run_agent():
    return asyncio.run(crew.kickoff())
# ✅ Fixed
@shared_task
def run_agent():
    return crew.kickoff()

If your agent logic depends on async I/O, push it behind a separate service or run it in a dedicated async worker process.

3) Creating and closing event loops manually

This breaks under load when code paths reuse the same process.

# ❌ Broken
loop = asyncio.new_event_loop()
asyncio.set_event_loop(loop)
result = loop.run_until_complete(crew.kickoff())
loop.close()
# ✅ Fixed
result = await crew.kickoff_async()

Manual loop handling belongs at the application boundary, not inside business logic.

4) Version mismatch between CrewAI and dependencies

Older CrewAI versions and mismatched langchain, pydantic, or Python versions can trigger runtime issues that look like loop errors.

crewai==0.x.x
python==3.12
langchain==0.x.x
pydantic==2.x.x

Check for:

  • Unsupported Python version for your CrewAI release
  • Mixed old/new agent APIs in the same codebase
  • Dependency conflicts after upgrading packages

Run:

pip show crewai langchain pydantic python-dotenv
pip freeze | grep -E "crewai|langchain|pydantic"

How to Debug It

  1. Read the exact traceback

    • If you see RuntimeError: This event loop is already running, you’re nesting loops.
    • If you see RuntimeError: Event loop is closed, something closed it too early.
    • If you see There is no current event loop in thread, you’re calling async code from a plain thread without setup.
  2. Find where kickoff() is called

    • Search for:
      • asyncio.run(
      • run_until_complete(
      • .kickoff()
      • .kickoff_async()
    • Check whether that call sits inside:
      • FastAPI/Starlette route handlers
      • Jupyter notebooks
      • Celery tasks
      • Background threads
  3. Confirm your runtime model

    • Sync app: use crew.kickoff()
    • Async app: use await crew.kickoff_async() if supported by your version
    • Worker process: keep it sync unless you own the event-loop lifecycle
  4. Print environment details before changing code

    import sys, asyncio, crewai
    
    print(sys.version)
    print(crewai.__version__)
    try:
        print(asyncio.get_running_loop())
    except RuntimeError as e:
        print(f"No running loop: {e}")
    

Prevention

  • Keep one execution model per boundary:
    • web request handlers stay async if needed,
    • workers stay sync unless there’s a strong reason otherwise.
  • Don’t call asyncio.run() inside libraries, routes, or task functions.
  • Pin and test dependency versions together before production deploys:
    • CrewAI,
    • Python,
    • LangChain-related packages,
    • your web framework.

If this only fails in production and not locally, assume runtime mismatch first: different server worker type, different Python version, or different dependency resolution. That’s where most CrewAI event-loop bugs hide.


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