# `ALLM.Error.ToolError`
[🔗](https://github.com/cykod/ALLM/blob/v0.3.0/lib/allm/error/tool_error.ex#L1)

Errors from tool handler execution.

Layer A — serializable. Note that `:cause` may carry a raised Exception
struct, the exit reason, or an invalid term the handler returned. Callers
should treat `:cause` as opaque and rely on `:reason` for programmatic
dispatch.

Note: the spec §20 atom `:tool_not_found` is represented here as
`:not_found` because the module name already carries the "tool" context.

See Phase 1 design §Sub-phase 1.1 for the closed reason enum.

# `reason`

```elixir
@type reason() ::
  :handler_raised
  | :handler_exit
  | :timeout
  | :invalid_return
  | :not_found
  | :encoding_failed
```

Closed set of tool-execution error reasons (spec §20).

# `t`

```elixir
@type t() :: %ALLM.Error.ToolError{
  __exception__: true,
  cause: term() | nil,
  message: String.t(),
  metadata: map(),
  reason: reason(),
  tool_call_id: String.t() | nil,
  tool_name: String.t() | nil
}
```

# `new`

```elixir
@spec new(
  reason(),
  keyword()
) :: t()
```

Build a `%ToolError{}` from a `reason` atom and optional keyword fields.

`opts` may include `:message`, `:tool_name`, `:tool_call_id`, `:cause`, and
`:metadata`. When `:message` is omitted, the default is
`"tool error: #{reason}"` — with a tool-name suffix
`"tool error (#{tool_name}): #{reason}"` when `:tool_name` is set.

Raises `ArgumentError` if `reason` is not one of the atoms in the closed
`t:reason/0` enum.

## Examples

    iex> err = ALLM.Error.ToolError.new(:handler_raised)
    iex> err.reason
    :handler_raised
    iex> Exception.message(err)
    "tool error: handler_raised"

    iex> err = ALLM.Error.ToolError.new(:timeout, tool_name: "search_web", tool_call_id: "call_1")
    iex> err.tool_call_id
    "call_1"
    iex> Exception.message(err)
    "tool error (search_web): timeout"

---

*Consult [api-reference.md](api-reference.md) for complete listing*
