Skip to main content
The RunContext is the central storage and state management system for your runs. Beyond storing spans, it enables data sharing between components, input/output manipulation, and hierarchical data access across parent-child relationships.

Accessing the RunContext

The RunContext is accessible from any callable within a Runnable’s execution using get_run_context(). This includes:
  • Main handlers: The main function that does the work (i.e. the function you pass to a Tool).
  • Default parameter callables: Functions used to compute default values at runtime (as shown in Runnables).
  • Lifecycle hooks: Functions that run before or after the main handler.
The RunContext.current_span() method returns the span object for the currently executing Runnable, containing all execution data - input parameters, output, timing, metadata, and any custom data you store on it. You get direct access to the live span object being built during execution. Here’s a simple example showing context access from a main handler:
Beyond current_span(), the RunContext provides .parent_span() and .step_span() for the parent runnable and for workflow steps. Workflow wiring is on Control flow.

Lifecycle Hooks

Beyond the main handler, Runnables support lifecycle hooks - functions that run at specific points during execution. These provide structured access points for context interaction and enable powerful data transformation patterns. Every Runnable supports two optional hooks. Both are parameterless.
  • pre_hook: Runs after Pydantic has validated the call and after the approval gate, and only when the handler is actually going to run.
  • post_hook: Runs after the handler completes.
Hooks share the RunContext with the handler. They can store custom data on the span, and a post-hook can replace the output.

What a pre-hook can change

A tool handler receives the kwargs that already passed validation. Those kwargs are fixed before the hook runs. Writing to span.input inside the hook updates the span record, and the handler still receives the validated kwargs. A required parameter the caller omitted fails validation, so the hook never runs. Supply defaults with default_params (applied before validation) and normalize values inside the handler. An agent is different. Agent.resolve_memory reads span.input after the pre-hook, so an agent pre-hook that sets span.input["prompt"] changes the user message for that turn.
Pre-hooks are a fit for:
  • State initialization: timestamps, request ids, and other metadata stored on the span
  • Observation: record the validated input for logging before the handler runs
  • Agent prompts: adjust span.input["prompt"] before resolve_memory builds the user message

Post-hooks: Processing Output After Completion

A post_hook runs after the handler and can read the input and the output. Assigning span.output replaces the handler’s return value. collect() returns that assigned value.
Output replacement: Assigning span.output in a post-hook replaces the handler’s return value. collect() returns the assigned value. Use this to transform results or apply a fixed response shape.
Post-hooks are perfect for:
  • Logging: Record execution details and results
  • Metadata Storage: Store processing metrics, timestamps, or analysis data
  • Output Modification: Transform or enrich the final result
  • Cleanup Tasks: Handle resource cleanup or state management
These simple examples show the basics. In practice, hooks excel at:
  • Agent prompts: rewrite span.input["prompt"] before the agent resolves memory (a webhook body, a transcript).
  • Tool kwargs: put omitted values in default_params, and normalize values inside the handler. The pre-hook cannot change those kwargs.
More advanced patterns in the Agents section.

Using Agents in Hooks

When you use an Agent inside a pre_hook or post_hook, you need to call .nest() to establish the proper hierarchical path for tracing and context management. This ensures the agent’s execution is correctly nested under the parent agent’s path.
Agents used as tools within another agent are automatically nested. However, agents used in hooks require manual nesting.
The .nest() method updates the agent’s path hierarchy, ensuring proper tracing structure (e.g., agent.agent_is_company instead of just agent_is_company) and correct span nesting under the parent. Nested agents still run with isolated memory — they do not inherit the parent’s conversation history (see Nested Agent Memory).

Early Exit with bail()

Use bail() to exit early from any Runnable execution when validation fails or conditions aren’t met. The bail() function raises an EarlyExit error that stops execution of the current runnable. You can use bail() in:
  • Handlers: Exit early from tool or agent handlers
  • Hooks: Exit early from pre_hook or post_hook functions
  • Default parameter callables: Exit early when computing default values
This is useful for input validation, filtering unwanted requests, or implementing conditional logic in any Runnable.