How to Handle Errors with Custom Error Policies in Jido AgentServer

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), 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) 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).

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.

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.

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.

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 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.

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:

@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, 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:

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.


# 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) 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) 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) 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) 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) 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.

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.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →