Fix issue #3871: kickoff_for_each() hangs after completion #3872
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Fix issue #3871: kickoff_for_each() hangs after completion
Summary
This PR fixes a threading issue where
kickoff_for_each()would hang indefinitely after successful completion. The root cause was that event handlers scheduled in the ThreadPoolExecutor were not being awaited, leaving non-daemon worker threads active and preventing process exit.Changes made:
_wait_for_event_handlers()helper method that safely waits on event bus futures with a 30-second timeout and proper error handlingkickoff()to wait on all three lifecycle event emissions (CrewKickoffStartedEvent, CrewKickoffCompletedEvent, CrewKickoffFailedEvent)is_tracing_disabled()helper to respectCREWAI_DISABLE_TRACING,CREWAI_DISABLE_TRACKING, andOTEL_SDK_DISABLEDenvironment variablesFiles changed:
lib/crewai/src/crewai/crew.py: Core fix for event handler waitinglib/crewai/src/crewai/events/listeners/tracing/utils.py: New tracing disable helperlib/crewai/tests/test_kickoff_for_each_hang.py: New test file with 4 test casesReview & Testing Checklist for Human
kickoff_for_each()with actual LLM calls and tracing enabled to verify the hang is fixed and there's no significant performance degradationCREWAI_DISABLE_TRACING=trueproperly disables tracing and doesn't break existing setupsRecommended test plan:
kickoff_for_each()with 5-10 inputs and verify it completes without hangingCREWAI_DISABLE_TRACING=trueto ensure tracing is properly disabledNotes
_run_sequential_processto avoid actual LLM calls, so they verify timing behavior but not full integrationkickoff()call fully processes its event handlers before returning, preventing accumulation of pending executor tasksSession details: