# How to Handle Errors with Custom Error Policies in Jido AgentServer

> Learn to handle errors with custom error policies in Jido AgentServer. Explore built-in and custom functions for robust error management. Delegate directives effectively.

- Repository: [agentjido/jido](https://github.com/agentjido/jido)
- Tags: how-to-guide
- Published: 2026-03-09

---

**Jido AgentServer delegates all error directives to `Jido.AgentServer.ErrorPolicy.handle/2`, which executes either built-in policies (`:log_only`, `:stop_on_error`, `{:emit_signal, cfg}`, `{:max_errors, n}`) or custom functions that return `{:ok, state}` or `{:stop, reason, state}`.**

Handling runtime failures gracefully is critical for production agent systems. In the **Jido** framework, the `AgentServer` isolates all side-effects—including error handling—inside a GenServer process. When an agent action fails, the system generates a `Jido.Agent.Directive.Error` that must be processed according to a configurable **error policy**. This article explains how to leverage built-in policies and implement custom error handlers to control exactly how your Jido agents respond to failures.

## Understanding Jido AgentServer Error Handling Architecture

Error handling in Jido AgentServer follows a delegation pattern. When the directive executor encounters an error directive, it immediately forwards the directive to `Jido.AgentServer.ErrorPolicy.handle/2` along with the current server state.

According to the source code in [[`lib/jido/agent_server/error_policy.ex`](https://github.com/agentjido/jido/blob/main/lib/jido/agent_server/error_policy.ex)](https://github.com/agentjido/jido/blob/main/lib/jido/agent_server/error_policy.ex), the `handle/2` function pattern-matches on `state.error_policy` to determine the appropriate response. The policy value is validated at startup by `Jido.AgentServer.Options.validate_error_policy/1` in [[`lib/jido/agent_server/options.ex`](https://github.com/agentjido/jido/blob/main/lib/jido/agent_server/options.ex)](https://github.com/agentjido/jido/blob/main/lib/jido/agent_server/options.ex) and stored in the runtime state defined in [[`lib/jido/agent_server/state.ex`](https://github.com/agentjido/jido/blob/main/lib/jido/agent_server/state.ex)](https://github.com/agentjido/jido/blob/main/lib/jido/agent_server/state.ex).

## Built-in Error Policies in Jido AgentServer

Jido provides five built-in policy variants that cover common error handling scenarios without requiring custom code.

### Log Only Policy (`:log_only`)

The default policy simply logs the error and continues execution. This is the safest option for development and non-critical production agents.

```elixir
Jido.AgentServer.start(
  agent: MyAgent,
  id: "my_agent",
  error_policy: :log_only
)

```

Under the hood, `ErrorPolicy.handle/2` calls `log_error/3` and returns `{:ok, state}`, allowing the GenServer to continue processing subsequent directives.

### Stop on Error Policy (`:stop_on_error`)

For critical agents where any failure should halt processing immediately, use the `:stop_on_error` policy.

```elixir
Jido.AgentServer.start(
  agent: MyAgent,
  id: "critical_agent",
  error_policy: :stop_on_error
)

```

When triggered, this policy logs the error and returns `{:stop, {:agent_error, error}, state}`, causing the AgentServer GenServer to terminate with the error reason.

### Emit Signal Policy (`{:emit_signal, dispatch_cfg}`)

This policy converts errors into Jido signals that can be consumed by other agents or external systems. It accepts a dispatch configuration map.

```elixir
dispatch_cfg = %{topic: "agent.errors", qos: 1}

Jido.AgentServer.start(
  agent: MyAgent,
  id: "signaling_agent",
  error_policy: {:emit_signal, dispatch_cfg}
)

```

The implementation in [`error_policy.ex`](https://github.com/agentjido/jido/blob/main/error_policy.ex) uses `build_error_signal/3` to construct a `Jido.Signal` with type `"jido.agent.error"`, then dispatches it asynchronously via `emit_error_signal/4`.

### Max Errors Policy (`{:max_errors, n}`)

Use this policy to implement circuit-breaker-like behavior, allowing a specific number of errors before stopping the agent.

```elixir
Jido.AgentServer.start(
  agent: MyAgent,
  id: "resilient_agent",
  error_policy: {:max_errors, 5}
)

```

This policy increments `state.error_count` using `State.increment_error_count/1`. If the count reaches the threshold, it stops the agent; otherwise, it logs a warning and continues.

## Creating Custom Error Policies in Jido AgentServer

When built-in policies are insufficient, you can provide a custom function that implements the `ErrorPolicy` contract.

### Function Signature and Return Contract

The custom function must match this type specification:

```elixir
@type result :: {:ok, State.t()} | {:stop, term(), State.t()}

@spec handle(ErrorDirective.t(), State.t()) :: result()

```

The function receives:
- `%Jido.Agent.Directive.Error{error: term(), context: term() | nil}` — the error directive containing the failure details and optional context
- `%Jido.AgentServer.State{}` — the current server state

### Safety Guarantees

According to the source in [`error_policy.ex`](https://github.com/agentjido/jido/blob/main/error_policy.ex), custom functions execute inside a `try … rescue … catch` block. If your function raises an exception, returns an unexpected value, or throws an error, the policy handler catches it, logs the failure, and returns `{:ok, state}` to keep the agent alive. This sandboxing prevents misbehaving custom policies from crashing the entire AgentServer.

### Example: Custom Monitoring Integration

Here is a complete example that reports errors to an external monitoring service while maintaining local error metrics:

```elixir
defmodule MyApp.CustomErrorPolicy do
  alias Jido.AgentServer.State
  
  def handle(%Jido.Agent.Directive.Error{error: err, context: ctx}, state) do
    # Report to external monitoring

    MyMonitoringService.report_error(
      agent_id: state.id,
      error: err,
      context: ctx,
      timestamp: DateTime.utc_now()
    )
    
    # Update custom metrics in state

    new_state = %State{
      state | 
      custom_error_count: (state.custom_error_count || 0) + 1,
      last_error_at: DateTime.utc_now()
    }
    
    # Decide whether to continue or stop based on error severity

    if critical_error?(err) do
      {:stop, {:critical_error, err}, new_state}
    else
      {:ok, new_state}
    end
  end
  
  defp critical_error?(%MyApp.CriticalException{}), do: true
  defp critical_error?(_), do: false
end

# Usage when starting the server

Jido.AgentServer.start(
  agent: MyAgent,
  id: "monitored_agent",
  error_policy: &MyApp.CustomErrorPolicy.handle/2
)

```

## Configuring Error Policies at Startup

The `error_policy` option is validated during server initialization. You can configure it via the `Jido.AgentServer.start/1` function or through your application supervisor.

```elixir

# Using a built-in atom policy

Jido.AgentServer.start(
  agent: MyAgent,
  id: "simple_agent",
  error_policy: :stop_on_error
)

# Using a tuple policy with configuration

Jido.AgentServer.start(
  agent: MyAgent,
  id: "circuit_breaker_agent",
  error_policy: {:max_errors, 10}
)

# Using a custom function (captured or anonymous)

custom_fn = fn error, state ->
  # Custom logic here

  {:ok, state}
end

Jido.AgentServer.start(
  agent: MyAgent,
  id: "custom_agent",
  error_policy: custom_fn
)

```

The validation logic in `Jido.AgentServer.Options.validate_error_policy/1` ensures that only valid policy shapes are accepted at runtime, preventing configuration errors from causing failures later during error handling.

## Key Source Files and Implementation Details

Understanding the source structure helps when debugging error policy behavior or extending functionality:

| File | Purpose |
|------|---------|
| [[`lib/jido/agent_server/error_policy.ex`](https://github.com/agentjido/jido/blob/main/lib/jido/agent_server/error_policy.ex)](https://github.com/agentjido/jido/blob/main/lib/jido/agent_server/error_policy.ex) | Core implementation of `handle/2` with pattern matching for all built-in policies and the custom function sandbox. |
| [[`lib/jido/agent_server/options.ex`](https://github.com/agentjido/jido/blob/main/lib/jido/agent_server/options.ex)](https://github.com/agentjido/jido/blob/main/lib/jido/agent_server/options.ex) | Validation of the `error_policy` option at startup via `validate_error_policy/1`. |
| [[`lib/jido/agent_server/state.ex`](https://github.com/agentjido/jido/blob/main/lib/jido/agent_server/state.ex)](https://github.com/agentjido/jido/blob/main/lib/jido/agent_server/state.ex) | Runtime state struct containing `error_policy` and `error_count` fields; provides `increment_error_count/1`. |
| [[`lib/jido/agent_server/directive_executors.ex`](https://github.com/agentjido/jido/blob/main/lib/jido/agent_server/directive_executors.ex)](https://github.com/agentjido/jido/blob/main/lib/jido/agent_server/directive_executors.ex) | Dispatches error directives to the policy module at line 46. |
| [[`lib/jido/agent_server.ex`](https://github.com/agentjido/jido/blob/main/lib/jido/agent_server.ex)](https://github.com/agentjido/jido/blob/main/lib/jido/agent_server.ex) | The main GenServer where directive processing and error flow management occur. |

## Summary

- **Jido AgentServer** delegates all error handling to the `Jido.AgentServer.ErrorPolicy` module via the `handle/2` function.
- Configure error behavior at startup using the `error_policy` option, validated by `Jido.AgentServer.Options.validate_error_policy/1`.
- Choose from built-in policies: `:log_only` (default), `:stop_on_error`, `{:emit_signal, config}`, or `{:max_errors, n}`.
- Implement custom policies by providing a function that accepts an `Error` directive and `State`, returning either `{:ok, state}` or `{:stop, reason, state}`.
- Custom functions execute inside a sandboxed `try/rescue/catch` block to prevent crashes from destabilizing the AgentServer.

## Frequently Asked Questions

### What is the default error policy in Jido AgentServer?

The default error policy is `:log_only`. When an error directive occurs, the server logs the error details and returns `{:ok, state}`, allowing the agent to continue processing subsequent directives without interruption. This policy is set automatically if you do not specify an `error_policy` option when starting the server.

### How do I stop a Jido AgentServer after a specific number of errors?

Use the `{:max_errors, n}` tuple policy, where `n` is the error threshold. The server maintains an internal `error_count` in its state (incremented via `State.increment_error_count/1`). Once the count reaches `n`, the agent stops with reason `{:max_errors_reached, n}`. Below the threshold, it logs warnings and continues.

```elixir
Jido.AgentServer.start(
  agent: MyAgent,
  id: "circuit_breaker",
  error_policy: {:max_errors, 5}
)

```

### Can I integrate external monitoring tools with Jido AgentServer error handling?

Yes, by providing a custom error policy function. The function receives the `%Jido.Agent.Directive.Error{}` struct and the current `%Jido.AgentServer.State{}`, allowing you to extract error details, agent IDs, and context. You can then call external APIs (e.g., Sentry, Datadog, or custom webhooks) before returning `{:ok, state}` or `{:stop, reason, state}`. The function executes inside a protected sandbox, so exceptions in your monitoring code won't crash the agent.

### What happens if my custom error policy function crashes or returns an invalid value?

The `ErrorPolicy.handle/2` function wraps custom function execution in a `try … rescue … catch` block. If your function raises an exception, returns a value other than `{:ok, state}` or `{:stop, reason, state}`, or throws an error, the policy handler catches the failure, logs an appropriate message, and returns `{:ok, state}`. This safety mechanism ensures that a buggy custom policy cannot inadvertently terminate the AgentServer or leave it in an inconsistent state.