-
Notifications
You must be signed in to change notification settings - Fork 5.7k
Description
Description
When a Crew executes a mix of synchronous and asynchronous tasks, all sync task outputs that occur before async tasks are silently
lost. This is caused by _process_async_tasks and _aprocess_async_tasks returning a new list, which then replaces the existing
task_outputs list instead of extending it.
The bug affects:
- _execute_tasks() (sync execution path)
- _aexecute_tasks() (async execution path)
- _handle_conditional_task()
- _ahandle_conditional_task()
Steps to Reproduce
from crewai import Agent, Task, Crew
agent = Agent(
role="Researcher",
goal="Research topics",
backstory="Expert researcher",
llm="gpt-4o-mini"
)
Create mixed sync/async tasks
task1 = Task(
description="Task 1 - sync",
expected_output="Output 1",
agent=agent,
async_execution=False # Sync
)
task2 = Task(
description="Task 2 - async",
expected_output="Output 2",
agent=agent,
async_execution=True # Async
)
task3 = Task(
description="Task 3 - sync",
expected_output="Output 3",
agent=agent,
async_execution=False # Sync
)
crew = Crew(agents=[agent], tasks=[task1, task2, task3])
result = crew.kickoff()
BUG: result.tasks_output only contains 2 items (task2, task3)
task1's output is LOST
print(f"Expected 3 outputs, got: {len(result.tasks_output)}")
Expected behavior
result.tasks_output should contain outputs from all 3 tasks in order: [task1_output, task2_output, task3_output]
Screenshots/Code snippets
Root cause in crew.py:
Line 1155 - BUG: Replaces instead of extends
task_outputs = self._process_async_tasks(futures, was_replayed)
_process_async_tasks creates a NEW list:
def _process_async_tasks(self, futures, was_replayed=False):
task_outputs: list[TaskOutput] = [] # <-- NEW empty list
for future_task, future, task_index in futures:
task_output = future.result()
task_outputs.append(task_output)
return task_outputs # <-- Only contains async outputs
Execution trace showing data loss:
| Step | Task | Action | task_outputs |
|---|---|---|---|
| 1 | Task1 (sync) | Execute & append | [output1] |
| 2 | Task2 (async) | Store future | [output1] |
| 3 | Task3 (sync) | Process futures | [output2] ← output1 LOST! |
| 4 | Task3 (sync) | Execute & append | [output2, output3] |
Operating System
macOS Big Sur
Python Version
3.10
crewAI Version
Current main branch (and likely all versions with async task support)
crewAI Tools Version
N/A - core crewAI bug
Virtual Environment
Venv
Evidence
The bug is in lib/crewai/src/crewai/crew.py at multiple locations:
- Line 960-962 (_aexecute_tasks)
- Line 976 (_aexecute_tasks)
- Line 990 (_ahandle_conditional_task)
- Line 1155 (_execute_tasks)
- Line 1169 (_execute_tasks)
- Line 1182 (_handle_conditional_task)
All use the pattern task_outputs = ...process_async_tasks(...) which replaces instead of extends.
Possible Solution
Change all occurrences from assignment to extend():
Before (BUG):
task_outputs = self._process_async_tasks(futures, was_replayed)
After (FIX):
task_outputs.extend(self._process_async_tasks(futures, was_replayed))
This mutates the existing list in-place, preserving previously accumulated sync task outputs.
Additional context
- Severity: Critical - silent data loss with no error raised
- Impact: Any crew mixing sync and async tasks loses outputs
- Detection difficulty: Very high - outputs appear valid but are incomplete
- Fix complexity: Low - single-character change at each location (= → .extend()