Repository navigation
fix(events): route aemit through dependency-aware dispatch - #8916
xuan (yuee3) wants to merge 1 commit into
Conversation
aemit() never consulted _handler_dependencies, so events emitted via the async entry point bypassed dependency-aware dispatch: async depends_on chains ran concurrently out of order, and sync handlers that async handlers depended on never executed. Route aemit through _emit_with_dependencies when dependencies are registered, matching the emit() and replay() dispatch paths.
Strix Security Review✅ No open security findings. PR summary: Makes the async event-bus entry point `CrewAIEventsBus.aemit()` honor `depends_on` handler registrations, routing dependency-aware events through the same level-by-level dispatcher that `emit()` and `replay()` already use, while leaving the no-dependency async-only path unchanged. Adds two tests covering async dependency ordering and sync dependency execution under `aemit`. Review summaryThe diff is a 10-line dispatch fix in Ruled out (2)
Review 1 on this PR · Reviewed by Strix |
Fixes #8913
Problem
CrewAIEventsBus.aemit()never consulted_handler_dependencies, so events emitted through the async entry point always bypassed dependency-aware dispatch and went straight toasyncio.gatherover all registered async handlers. The other two dispatch entry points (emit()andreplay()) both checkhas_dependenciesand route to_emit_with_dependencies();aemit()was the only one that did not.Consequences for events emitted via
aemit():depends_onchains execute concurrently and out of order instead of level by level.Fix
In
aemit(), readhas_dependenciesinside the existingr_locked()block (same pattern asemit()), and when true, callawait self._emit_with_dependencies(...)directly (aemit already runs on the event loop thread, so norun_coroutine_threadsafeis needed). The no-dependency behavior is unchanged: only async handlers run, concurrently.Relation to #8716
Completely independent: #8716 fixes mixed sync/async handler future tracking in
emit()(flush() semantics). This PR only touchesaemit()'s dispatch path — a different function and a different failure mode.Verification
lib/crewai/tests/events/test_depends.py:test_aemit_respects_async_dependency_chain— async dependency chain executes independs_onorder underaemit(failed before the fix, passes after).test_aemit_runs_sync_dependency_handlers— sync handler that an async handler depends on is executed underaemit(failed before the fix, passes after).test_aemit_ignores_dependenciesrenamed totest_aemit_without_dependencies_runs_async_onlyto reflect preserved no-dependency semantics.test_depends.py: 12/12 passed.tests/eventssuite: only pre-existing environment failures (litellm not installed, lancedbColumnOrderingimport error); the failure set is identical with and without this change (29 failed / 166 passed both ways, same files), so no regressions.Note
Medium Risk
Changes core event dispatch ordering for async emit paths; wrong ordering could affect listeners that rely on depends_on, but the change aligns with existing emit/replay behavior.
Overview
CrewAIEventsBus.aemit()now matchesemit()andreplay()when handlers usedepends_on: it checks_handler_dependenciesandawaits_emit_with_dependencies()instead of alwaysasyncio.gather-ing async handlers.That fixes async
depends_onchains running out of order underaemit, and sync prerequisite handlers being skipped while dependents still ran. When no dependencies are registered, behavior is unchanged—only async handlers run, concurrently.Tests rename the old “ignores dependencies” case and add coverage for async chains and sync→async dependency under
aemit.Reviewed by Cursor Bugbot for commit 2bc43f5. Bugbot is set up for automated code reviews on this repo. Configure here.