Most tool error handling is written for humans reading logs. But when an agent calls the tool, the first reader of the error is the model, and what it does next depends entirely on what the message says. Compare two responses to the same bad call:
Error: 400 Bad RequestInvalid date_range: end (2024-01-05) is before start (2024-03-01). Dates must be ISO 8601 and end must not precede start. Retry with the two values swapped if that was the intent.
The first invites a blind retry or a hallucinated workaround. The second lets the agent fix the call in one step.
What a recoverable error contains
What went wrong, specifically. Name the argument and quote the offending value. "Invalid input" is a dead end; "limit must be between 1 and 100, got 500" is a fix.
Whether retrying can help. Distinguish three classes and say which one it is:
- *Caller error:* the arguments were wrong; change them.
- *Transient error:* the same call may succeed later; wait, then retry.
- *Permanent error:* nothing the agent does will work; stop and report.
Without this, agents either retry permanent failures in a loop or give up on transient ones. Making the class explicit, perhaps as a field like retryable: false, also lets your own code enforce a retry cap.
The next valid move. If the agent asked for a resource that does not exist, list close matches or say how to search for valid ones. If a precondition is unmet, name the tool that satisfies it.
What to keep out of the message
Do not return raw stack traces, internal hostnames, SQL, or file paths. They waste context, they can leak details you would not want echoed into logs or user-facing replies, and models tend to latch onto incidental details in a trace. Log the full detail server-side under a correlation ID and return only the ID plus the actionable summary.
Keep messages short and consistent in shape. If every tool returns errors as { "ok": false, "kind": "caller_error", "message": "...", "hint": "..." }, the agent learns the pattern once and your handler logic is trivial to test.
Test the failure path on purpose
Error messages are rarely exercised, so they rot. Add a small set of tests that deliberately make bad calls and assert two things: the response is well-formed, and a model given only that response can produce a corrected call. The second check can be a handful of recorded cases run against your model; if the agent cannot recover from your message, a human reader would probably struggle too.
Treat error text as part of the tool's interface. It is often the cheapest improvement available to an agent that seems unreliable.
Comments (0)
No comments yet — be the first.
Sign in to leave a comment.