Skip to main content
Execution hooks provide fine-grained control over the runtime behavior of your CrewAI agents. Unlike kickoff hooks that run before and after crew execution, execution hooks intercept specific operations during execution — from the moment a run starts, through every model call, tool call, and task or flow-method step, down to the final output. Hooks are written with the @on decorator: one registration API and one contract cover every interception point in the framework.
The point-specific decorators (@before_llm_call, @after_tool_call, …) keep working unchanged — they are adapters over the same engine. See Point-specific decorators (legacy) at the end of this page.

The contract

Every hook is a synchronous callable that receives a single typed context:
A hook may do any of four things:

Registering hooks

Use @on for global hooks. It accepts agents= / tools= filters to scope a hook to specific agent roles or tool names:
Applied to a method inside a @CrewBase class, @on registers a crew-scoped hook, active only while that crew runs:

Interception point catalog

Each family has a detailed guide covering its context schema, payload semantics, and examples.

Execution boundaries

Model boundaries & tool boundaries

At these four points the hook receives the rich legacy context directly as its argument — there is no separate ctx.payload. Mutate ctx.messages / ctx.tool_input in place, and return a string from a post hook to replace the response / tool result.

Step points

PRE_STEP / POST_STEP carry ctx.kind ("task" or "flow_method") and ctx.step_name.

Aborting an operation

HookAborted carries a reason and an optional source. The source defaults to the aborting hook when omitted, which is useful for telemetry and failure messages:

Composition, ordering, and fail-open

  • Multiple hooks on the same point run in registration order, global hooks first, then execution-scoped hooks. Legacy hooks registered for the same point participate in the same chain.
  • The (possibly mutated) payload flows from one hook to the next.
  • HookAborted propagates by design and stops the chain.
  • Any other exception raised by a hook is swallowed (fail-open) so a single buggy hook can’t crash a run.
  • When no hook is registered for a point, dispatch is a single dict lookup (no-op fast path), so unused points cost effectively nothing.

Common patterns

Safety guardrails

Human-in-the-loop approval

Sanitizing outputs

A non-None return value replaces the interceptable value, so transformations are plain return statements:

Observing steps

Telemetry

Whenever a point actually dispatches to at least one hook, CrewAI emits a HookDispatchedEvent on the event bus with the point, the outcome (proceeded / modified / aborted), the hook count, the duration, and — for aborts — the reason and source. The no-op fast path emits nothing.

Managing hooks in tests

Global hooks persist for the lifetime of the process. Reset them between tests:

Best practices

  1. Keep hooks focused — one clear responsibility per hook; register several small hooks rather than one that does everything.
  2. Keep hooks fast — hooks run on every dispatch of their point; avoid heavy computation and lazy-import heavy dependencies.
  3. Prefer scoping — use agents= / tools= filters and crew-scoped registration instead of unconditional global hooks.
  4. Abort loudly — raise HookAborted with a meaningful reason and source; that context surfaces in error messages and telemetry. Remember that any other exception is swallowed (fail-open), so don’t rely on raising ValueError to stop a run.

Point-specific decorators (legacy)

Before @on, LLM and tool calls were hooked with dedicated decorator pairs. These keep working unchanged — they are adapters over the same dispatcher, so they compose with @on hooks in the same registration-order chain:
Differences from @on:
  • They cover only the four model/tool points — no execution boundaries, no steps.
  • Blocking is return False, with no abort reason or source attached.
  • They receive the same rich contexts — LLMCallHookContext (with full executor access) and ToolCallHookContext — that @on hooks receive at the model/tool points.
  • Crew-scoping works the same way: apply the decorator to a method inside a @CrewBase class.
  • They support the same agents= / tools= filters.
You might still prefer them for existing codebases that already use return False semantics, or when you want the point-specific typed signatures. For the detailed guides — context attributes, patterns, and management APIs (register_* / unregister_* / clear_*) — see: