Skip to content

fix: dispatch execution_end hook on failed crew and flow executions#6607

Merged
lucasgomide merged 3 commits into
mainfrom
luzk/execution-end-on-failure
Jul 21, 2026
Merged

fix: dispatch execution_end hook on failed crew and flow executions#6607
lucasgomide merged 3 commits into
mainfrom
luzk/execution-end-on-failure

Conversation

@lucasgomide

@lucasgomide lucasgomide commented Jul 21, 2026

Copy link
Copy Markdown
Contributor

The execution_end global hook only dispatched after a successful run, so consumers observing execution boundaries never saw failed executions and could not distinguish outcomes. This dispatches it exactly once on both success and failure across the crew kickoff paths and the flow runtime, including the resume path. ExecutionEndContext gains additive status ("completed"/"failed") and error fields whose defaults preserve the current shape for existing consumers, and the original exception re-raises unchanged. Executions whose execution_start never dispatched still fire no execution_end, keeping the pairing invariant


Note

Medium Risk
Touches core kickoff/resume paths and extends the public hook context contract, but new fields default to completed/None and failure dispatch is guarded to be exactly-once without masking the original exception.

Overview
EXECUTION_END now runs once per execution on success and failure, so boundary hooks can observe failed runs instead of only happy paths. ExecutionEndContext adds status ("completed" / "failed") and error; defaults keep existing success behavior.

Crew kickoff (sync/async) and flow kickoff/resume wire a failure dispatch from exception handlers via _dispatch_execution_end_failure, with START/END pairing so EXECUTION_END is skipped if EXECUTION_START never ran or END already fired (including when a success-path END hook raises HookAborted). Flow uses per-invocation flags for reentrant kickoffs; crew uses instance flags.

Docs add an Observing Failures section; conformance tests cover crew/flow success and failure, async crew, reentrant flow, START abort, and END HookAborted exactly-once behavior.

Reviewed by Cursor Bugbot for commit 51ad6b4. Bugbot is set up for automated code reviews on this repo. Configure here.

The `execution_end` interception point only fired after a successful
kickoff, so consumers never learned about failed runs. Crew kickoff
paths (`kickoff`/`akickoff`) and the flow runtime (`kickoff_async`,
`resume_async`) now dispatch it on the failure path too, with new
additive `status` ("completed"/"failed") and `error` fields on
`ExecutionEndContext`. Pairing flags guarantee exactly-once dispatch,
keep the start/end pairing invariant, and the original exception
propagates unchanged.
@linear

linear Bot commented Jul 21, 2026

Copy link
Copy Markdown

OSS-95

@coderabbitai

coderabbitai Bot commented Jul 21, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

Execution-end interception now dispatches exactly once for successful and failed Crew and Flow executions. Contexts expose status and errors, lifecycle flags guard pairing, failure dispatch is non-raising, and documentation and conformance tests cover synchronous, asynchronous, resume, and aborted-hook paths.

Changes

Execution end hook lifecycle

Layer / File(s) Summary
Execution end context contract
lib/crewai/src/crewai/hooks/contexts.py, docs/edge/en/learn/execution-boundary-hooks.mdx
ExecutionEndContext now includes completed/failed status and failure errors, with documentation for payloads and dispatch behavior.
Crew execution-end dispatch
lib/crewai/src/crewai/crew.py, lib/crewai/src/crewai/crews/utils.py
Crew kickoff initializes lifecycle flags, emits successful end hooks once, and conditionally emits failed end hooks for synchronous and asynchronous errors.
Flow kickoff and resume dispatch
lib/crewai/src/crewai/flow/runtime/__init__.py
Flow kickoff and resume paths track start/end pairing and dispatch failed EXECUTION_END contexts through a guarded helper.
Execution-end conformance tests
lib/crewai/tests/hooks/test_interception_conformance.py
Tests cover successful and failed Crew and Flow executions, async failure, aborted starts, context fields, reentrant invocations, and aborting end hooks.

Sequence Diagram(s)

sequenceDiagram
  participant Execution as Crew or Flow execution
  participant Hooks as Interception hooks
  participant Context as ExecutionEndContext
  Execution->>Hooks: Dispatch EXECUTION_START
  Hooks-->>Execution: Start dispatch completes
  Execution->>Context: Create completed or failed outcome
  Execution->>Hooks: Dispatch EXECUTION_END once
  Hooks-->>Execution: Complete or abort end dispatch
Loading

Suggested reviewers: vinibrsl, vinibrsl

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Title check ✅ Passed The title clearly summarizes the main change: dispatching execution_end on failed crew and flow executions.
Description check ✅ Passed The description matches the changeset and accurately describes the new failure dispatch behavior and context fields.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch luzk/execution-end-on-failure

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

Comment thread lib/crewai/src/crewai/flow/runtime/__init__.py Outdated

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
lib/crewai/src/crewai/flow/runtime/__init__.py (1)

2083-2113: 🩺 Stability & Availability | 🟠 Major | 🏗️ Heavy lift

Reentrant kickoff_async can overwrite the pairing flags. _execution_start_dispatched and _execution_end_dispatched live on self, so a nested call on the same Flow can reset them for the outer call. That can suppress the outer failure EXECUTION_END if the inner run succeeds, or if the inner EXECUTION_START aborts. Move this pairing state to per-call storage so reentrant executions don’t interfere.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@lib/crewai/src/crewai/flow/runtime/__init__.py` around lines 2083 - 2113,
Move execution hook pairing state out of the shared Flow instance and into
per-invocation locals within kickoff_async. Track whether the current call
dispatched EXECUTION_START and whether its EXECUTION_END was dispatched, and
update all related checks and assignments to use that local state so nested
kickoff_async calls cannot affect the outer execution’s failure or completion
handling.
🧹 Nitpick comments (2)
lib/crewai/src/crewai/crew.py (1)

1961-1982: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

_dispatch_execution_end_failure is duplicated verbatim across Crew and Flow. Both classes implement the identical no-op-guard + non-raising-dispatch logic for the pairing invariant; the only difference is crew=self vs. flow=self in the constructed ExecutionEndContext.

  • lib/crewai/src/crewai/crew.py#L1961-L1982: keep as the reference implementation, or extract the shared body (guard check, try/except-swallow, context construction with **{owner_kwarg: self}) into a small shared helper/mixin both Crew and Flow call into.
  • lib/crewai/src/crewai/flow/runtime/__init__.py#L2463-L2484: replace this copy with a call into the shared helper once extracted, so future changes to the pairing/no-raise semantics can't drift between Crew and Flow.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@lib/crewai/src/crewai/crew.py` around lines 1961 - 1982, The duplicated
_dispatch_execution_end_failure implementations in
lib/crewai/src/crewai/crew.py:1961-1982 and
lib/crewai/src/crewai/flow/runtime/__init__.py:2463-2484 should share one
implementation. Extract the guard, exactly-once flag update, non-raising
dispatch, and ExecutionEndContext construction into a helper or mixin accepting
the owner keyword, keep Crew using crew=self, and replace Flow’s copy with a
call using flow=self.
lib/crewai/tests/hooks/test_interception_conformance.py (1)

162-291: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick win

Missing coverage for Crew.akickoff() and Flow.resume_async() failure dispatch.

test_crew_kickoff_async_failure_fires_failed_once only exercises kickoff_async (which just thread-wraps sync kickoff), not the native-async akickoff() path that has its own separate _dispatch_execution_end_failure(e) call site (crew.py line 1269). Similarly, no test covers Flow.resume_async/_resume_async_body's failure path (flow/runtime/__init__.py lines 1339-1341), which has its own distinct flag-priming logic. The PR objective explicitly calls out both akickoff and resume_async as covered paths — worth adding direct tests for each to close the gap, especially given how easy the flag-based pairing is to get wrong (see the reentrancy concern raised in flow/runtime/__init__.py).

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@lib/crewai/tests/hooks/test_interception_conformance.py` around lines 162 -
291, Add direct failure tests for Crew.akickoff and Flow.resume_async, rather
than relying on Crew.kickoff_async. In TestExecutionEndOnFailure, trigger each
native async path with a failing task or flow execution, assert the original
exception is reraised, and verify EXECUTION_END is dispatched exactly once with
failed status and the captured error. Ensure the Flow.resume_async test
exercises its resume-specific failure path.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Outside diff comments:
In `@lib/crewai/src/crewai/flow/runtime/__init__.py`:
- Around line 2083-2113: Move execution hook pairing state out of the shared
Flow instance and into per-invocation locals within kickoff_async. Track whether
the current call dispatched EXECUTION_START and whether its EXECUTION_END was
dispatched, and update all related checks and assignments to use that local
state so nested kickoff_async calls cannot affect the outer execution’s failure
or completion handling.

---

Nitpick comments:
In `@lib/crewai/src/crewai/crew.py`:
- Around line 1961-1982: The duplicated _dispatch_execution_end_failure
implementations in lib/crewai/src/crewai/crew.py:1961-1982 and
lib/crewai/src/crewai/flow/runtime/__init__.py:2463-2484 should share one
implementation. Extract the guard, exactly-once flag update, non-raising
dispatch, and ExecutionEndContext construction into a helper or mixin accepting
the owner keyword, keep Crew using crew=self, and replace Flow’s copy with a
call using flow=self.

In `@lib/crewai/tests/hooks/test_interception_conformance.py`:
- Around line 162-291: Add direct failure tests for Crew.akickoff and
Flow.resume_async, rather than relying on Crew.kickoff_async. In
TestExecutionEndOnFailure, trigger each native async path with a failing task or
flow execution, assert the original exception is reraised, and verify
EXECUTION_END is dispatched exactly once with failed status and the captured
error. Ensure the Flow.resume_async test exercises its resume-specific failure
path.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 06ffb1c3-41ab-4257-a383-da0c3b24bfa7

📥 Commits

Reviewing files that changed from the base of the PR and between 40279e3 and c7b2c00.

📒 Files selected for processing (6)
  • docs/edge/en/learn/execution-boundary-hooks.mdx
  • lib/crewai/src/crewai/crew.py
  • lib/crewai/src/crewai/crews/utils.py
  • lib/crewai/src/crewai/flow/runtime/__init__.py
  • lib/crewai/src/crewai/hooks/contexts.py
  • lib/crewai/tests/hooks/test_interception_conformance.py

@mintlify

mintlify Bot commented Jul 21, 2026

Copy link
Copy Markdown

Preview deployment for your docs. Learn more about Mintlify Previews.

Project Status Preview Updated (UTC)
crewai 🟢 Ready View Preview Jul 21, 2026, 5:45 PM

💡 Tip: Enable Workflows to automatically generate PRs for you.

Reentrant kickoffs on the same Flow instance are supported (usage
aggregation already accommodates them), but the instance-level pairing
booleans let an inner kickoff's completion mark the outer execution as
ended, skipping the outer failure's `execution_end`. The pairing state
now lives in each `kickoff_async` invocation's locals, and the resume
path passes a per-invocation holder into `_resume_async_body`. Crew
keeps its instance flags since crew kickoffs are not reentrant on the
same instance (`kickoff_for_each` copies the crew).

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes using default effort and found 1 potential issue.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit 51ad6b4. Configure here.

Comment thread lib/crewai/src/crewai/flow/runtime/__init__.py
@lucasgomide
lucasgomide merged commit 3bb8753 into main Jul 21, 2026
66 of 90 checks passed
@lucasgomide
lucasgomide deleted the luzk/execution-end-on-failure branch July 21, 2026 19:25
magiccao pushed a commit to magiccao/crewAI that referenced this pull request Jul 22, 2026
…callbacks

Resolved conflict in lib/crewai/src/crewai/crews/utils.py: kept the
_execution_start_dispatched/_execution_end_dispatched flag pairing
introduced in crewAIInc#6607 (set False before dispatch, True after), while
preserving the helper-function refactor that returns start_ctx.payload
from _dispatch_execution_start.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants