crewAI icon indicating copy to clipboard operation
crewAI copied to clipboard

Fix asyncio hanging issue when using crew.kickoff() with asyncio.to_thread()

Open devin-ai-integration[bot] opened this issue 3 months ago • 2 comments

Fix asyncio hanging issue when using crew.kickoff() with asyncio.to_thread()

Summary

Fixes issue #3730 where crews would hang indefinitely when run with asyncio.to_thread(crew.kickoff) if they used async tools.

Root Cause: The tools code was calling asyncio.run() to execute async tool functions. When a crew is run via asyncio.to_thread(), it executes in a thread spawned by an existing event loop. Calling asyncio.run() in this context fails because it tries to create a new event loop in a thread that's conceptually part of an already-running loop, causing a hang.

Solution: Added a run_coroutine_sync() utility that:

  • Uses asyncio.run() when no event loop is running (normal case)
  • When a loop IS running, spawns a new thread with its own event loop to run the coroutine

Changes:

  • Added src/crewai/utilities/asyncio_utils.py with run_coroutine_sync() helper
  • Updated CrewStructuredTool.invoke() to use the new helper (2 call sites)
  • Updated BaseTool.run() to use the new helper (1 call site)
  • Added comprehensive test suite in tests/test_asyncio_tools.py

Review & Testing Checklist for Human

⚠️ Risk Level: MEDIUM-HIGH - This modifies a core execution path affecting all async tool usage

  • [ ] Review the threading approach in run_coroutine_sync() - Is spawning a new thread with a new event loop the right solution? Consider:

    • Performance implications (creates a thread for every async tool call when in nested async context)
    • Potential thread safety issues
    • Resource leak risks (event loop cleanup)
    • Whether there's a better alternative (e.g., using asyncio.get_event_loop().run_until_complete() or other approaches)
  • [ ] Test the actual issue scenario end-to-end - The new tests mock Agent.execute_task, so they don't verify full integration. Test manually:

    # Run this with a real LLM configured
    import asyncio
    from crewai import Agent, Crew, Task
    from crewai.tools import tool
    
    @tool
    async def test_tool(x: str) -> str:
        """Test async tool"""
        await asyncio.sleep(0.1)
        return f"Result: {x}"
    
    crew = Crew(
        agents=[Agent(role="Test", goal="Test", backstory="Test")],
        tasks=[Task(description="Use test_tool", expected_output="Result", tools=[test_tool])]
    )
    
    async def run():
        result = await asyncio.to_thread(crew.kickoff)
        print(result)
    
    asyncio.run(run())
    

    Verify it completes without hanging.

  • [ ] Verify the lock file regeneration didn't break anything - The uv.lock was completely regenerated (not part of core fix). Run the full test suite to ensure no regressions.

  • [ ] Check exception handling - Verify that exceptions raised in async tools are properly propagated with full tracebacks through the new threading approach.

Notes

  • Issue reported by user with local Ollama setup experiencing hangs when running crew in background with asyncio.to_thread()
  • The existing kickoff_async() method already worked correctly; this fixes the manual asyncio.to_thread(crew.kickoff) pattern
  • All new tests pass, and existing thread safety tests pass

Link to Devin run: https://app.devin.ai/sessions/fa61a1f74deb410faa495932f4a86cad
Requested by: João ([email protected])

🤖 Devin AI Engineer

I'll be helping with this pull request! Here's what you should know:

✅ I will automatically:

  • Address comments on this PR. Add '(aside)' to your comment to have me ignore it.
  • Look at CI failures and help fix them

Note: I can only respond to comments from users who have write access to this repository.

⚙️ Control Options:

  • [ ] Disable automatic comment and CI monitoring

Just stumbled upon this problem, +1 for merging this PR <3

Saicheg avatar Nov 03 '25 16:11 Saicheg

When is this getting merged and released?

sameermahajan avatar Nov 18 '25 12:11 sameermahajan