# How to Handle and Customize Error Handling with RunErrorHandlers in openai-agents-python

> Master error handling in openai-agents-python with RunErrorHandlers. Intercept errors like MaxTurnsExceeded and customize outputs for a smoother user experience.

- Repository: [OpenAI/openai-agents-python](https://github.com/openai/openai-agents-python)
- Tags: how-to-guide
- Published: 2026-04-17

---

**RunErrorHandlers in openai-agents-python provide pluggable hooks that let you intercept runtime errors like `MaxTurnsExceeded` and return custom final outputs, with optional control over whether the fallback appears in conversation history.**

The openai-agents-python SDK includes a powerful extension point for graceful degradation when agent runs hit limits. By implementing **RunErrorHandlers**, you can customize how the framework responds to specific runtime errors, ensuring your agents fail gracefully with meaningful outputs rather than abrupt exceptions.

## What Are RunErrorHandlers?

**RunErrorHandlers** are pluggable hooks defined in [`src/agents/run_error_handlers.py`](https://github.com/openai/openai-agents-python/blob/main/src/agents/run_error_handlers.py) that let you decide what happens when the agent runtime raises a known error. Currently, the framework only supports handling `MaxTurnsExceeded` through the `"max_turns"` key.

The handler receives a rich snapshot of the run via **RunErrorHandlerInput** (which contains **RunErrorData**) and can return several types of values:

- **RunErrorHandlerResult** – Provides full control with explicit `final_output` and `include_in_history` fields.
- **dict** – A shortcut containing `final_output` and optionally `include_in_history`; converted automatically to `RunErrorHandlerResult`.
- **Any other value** – Treated as `final_output` with default settings (`include_in_history=True`).
- **None** – Indicates no handling; the runtime treats the error as unhandled and aborts.

The core type definitions live in **[`src/agents/run_error_handlers.py`](https://github.com/openai/openai-agents-python/blob/main/src/agents/run_error_handlers.py)**:

- `RunErrorData` captures the snapshot of the run state [[source](https://github.com/openai/openai-agents-python/blob/main/src/agents/run_error_handlers.py#L16-L27)].
- `RunErrorHandlerInput` wraps the error, context, and run data [[source](https://github.com/openai/openai-agents-python/blob/main/src/agents/run_error_handlers.py#L29-L34)].
- `RunErrorHandlerResult` defines the expected return shape [[source](https://github.com/openai/openai-agents-python/blob/main/src/agents/run_error_handlers.py#L36-L41)].
- `RunErrorHandlers` is a `TypedDict` keyed by error kind [[source](https://github.com/openai/openai-agents-python/blob/main/src/agents/run_error_handlers.py#L49-L55)].

## How Handlers Are Invoked in the Runtime

When the turn counter exceeds `max_turns`, the run loop in **[`src/agents/run_internal/run_loop.py`](https://github.com/openai/openai-agents-python/blob/main/src/agents/run_internal/run_loop.py)** triggers the error handling pipeline.

At lines 874-904, the code builds a `RunErrorData` snapshot and calls `resolve_run_error_handler_result`:

```python

# src/agents/run_internal/run_loop.py#L874-L904

if current_turn > max_turns:
    ...
    run_error_data = build_run_error_data(...)
    handler_result = await resolve_run_error_handler_result(
        error_handlers=error_handlers,
        error=max_turns_error,
        context_wrapper=context_wrapper,
        run_data=run_error_data,
    )

```

The helper function **`resolve_run_error_handler_result`** in [`src/agents/run_internal/error_handlers.py`](https://github.com/openai/openai-agents-python/blob/main/src/agents/run_internal/error_handlers.py) performs three critical operations:

1. Detects the `"max_turns"` handler in the provided mapping.
2. Awaits the result if the handler is a coroutine.
3. Normalizes the return value into a `RunErrorHandlerResult`.

If the handler returns a result, the runtime validates the `final_output` against the agent’s output schema using **`validate_handler_final_output`** (lines 80-106), formats it to a string via **`format_final_output_text`**, and injects the synthesized message back into the run while respecting the `include_in_history` setting.

## Attaching Custom Error Handlers

You pass error handlers to the `error_handlers` argument of any public runner entry point in **[`src/agents/run.py`](https://github.com/openai/openai-agents-python/blob/main/src/agents/run.py)**:

- **`Runner.run`** and **`Runner.run_streamed`** (async) [[source](https://github.com/openai/openai-agents-python/blob/main/src/agents/run.py#L191-L204)].
- **`Runner.run_sync`** (synchronous) [[source](https://github.com/openai/openai-agents-python/blob/main/src/agents/run.py#L274-L287)].

Here is a basic synchronous handler that returns a fallback string when turns are exhausted:

```python
from agents import Runner, Agent
from agents.run_error_handlers import RunErrorHandlers

agent = Agent(name="summarizer", output_type=str)

def max_turns_handler(data):
    # Access run state via data.run_data (input, history, new items, etc.)

    return "I ran out of turns, here is a brief answer."

handlers: RunErrorHandlers = {"max_turns": max_turns_handler}

result = Runner.run_sync(
    starting_agent=agent,
    input="Give me a step-by-step guide to bake a cake.",
    max_turns=2,  # Force the error

    error_handlers=handlers,
)

print(result.final_output)

# → I ran out of turns, here is a brief answer.

```

## Async Handlers and Structured Output Validation

Handlers may be regular functions or **coroutines**. The runtime automatically awaits async handlers, making it easy to perform I/O operations like database lookups or logging:

```python
import asyncio
from agents import Runner, Agent
from agents.run_error_handlers import RunErrorHandlers

agent = Agent(name="structured", output_type=dict)

async def async_handler(data):
    await asyncio.sleep(0.1)  # Simulate I/O

    return {"final_output": {"status": "timeout", "summary": "Task incomplete."}}

handlers: RunErrorHandlers = {"max_turns": async_handler}

result = await Runner.run(
    starting_agent=agent,
    input="Explain the theory of relativity in depth.",
    max_turns=1,
    error_handlers=handlers,
)

```

If the `final_output` does not match the agent’s declared `output_type`, **`validate_handler_final_output`** raises a `UserError`. This prevents corrupted structured outputs from propagating through your application.

## Controlling Conversation History

The **`include_in_history`** boolean determines whether the fallback message persists in the agent's history:

- **`True`** (default): The synthesized message appears as a normal assistant message. Use this when the fallback represents a logical continuation, such as a concise summary of partial work.
- **`False`**: The message returns to the caller but is excluded from history. Use this for generic error messages, debugging information, or when you want the output to remain opaque to downstream tools.

```python
def silent_handler(data):
    return {
        "final_output": "Sorry – the operation timed out.",
        "include_in_history": False,  # Excludes from conversation history

    }

handlers = {"max_turns": silent_handler}

```

## Summary

- **RunErrorHandlers** are defined in [`src/agents/run_error_handlers.py`](https://github.com/openai/openai-agents-python/blob/main/src/agents/run_error_handlers.py) and provide a `TypedDict` interface for customizing error recovery.
- The runtime invokes handlers via `resolve_run_error_handler_result` in [`src/agents/run_internal/error_handlers.py`](https://github.com/openai/openai-agents-python/blob/main/src/agents/run_internal/error_handlers.py) when `max_turns` is exceeded.
- Handlers can be sync or async, and must return values compatible with the agent’s `output_type` to pass validation.
- **Runner.run**, **run_sync**, and **run_streamed** accept the `error_handlers` mapping to inject your custom logic.
- Use **`include_in_history=False`** to return fallback outputs without polluting the conversation context.

## Frequently Asked Questions

### What errors can RunErrorHandlers currently handle in openai-agents-python?

Currently, RunErrorHandlers only support the `"max_turns"` key, which maps to the `MaxTurnsExceeded` error. The framework may expand to support additional error types in future releases, but the current implementation focuses exclusively on graceful handling when agents exceed their turn limits.

### Can I use async functions as RunErrorHandlers?

Yes. The runtime in [`src/agents/run_internal/error_handlers.py`](https://github.com/openai/openai-agents-python/blob/main/src/agents/run_internal/error_handlers.py) automatically detects if your handler is a coroutine and awaits it. This allows you to perform asynchronous operations like logging to external services, querying databases, or fetching context before returning the final output.

### What happens if my handler returns a value that doesn't match the agent's output_type?

The runtime validates handler outputs using `validate_handler_final_output` in [`src/agents/run_internal/error_handlers.py`](https://github.com/openai/openai-agents-python/blob/main/src/agents/run_internal/error_handlers.py). If the returned value doesn't conform to the agent's declared schema, the SDK raises a `UserError` immediately. This validation ensures structured outputs remain consistent even when error handlers intervene.

### When should I set include_in_history to False?

Set `include_in_history=False` when your fallback message is purely informational (like "Service temporarily unavailable") or contains debugging data that downstream tools shouldn't see. Set it to `True` when the fallback represents a valid partial completion of the task, such as a summary of work done before hitting the turn limit, allowing the conversation to continue naturally.