Open source

The three ways an agent should
respond to a failed tool call

An LLM agent is good at deciding what to do and bad at handling what happens when the call fails. Most agents react to every failure the same way — re-prompt the model, or blindly retry. Both are wrong most of the time. Here's the smaller idea that fixes it, and the tiny open-source library that implements it.

klanex engineering · August 2026 · ~6 min read

When a tool call fails, the model gets back a raw 403, a swallowed exception, or a two-page HTML error, and reacts on instinct. The instinct is almost always one of two moves — re-prompt itself, or retry — and both are usually the wrong one:

The mistake underneath all three is the same: reacting to the HTTP status instead of to what it means for the agent.

The question isn't the status code

A 429 and a 400 are both "the call failed," but they demand opposite responses: wait and try again unchanged, versus stop and fix the request. The useful axis was never the number — it's the reaction the number should trigger. There turn out to be exactly three, plus an honest fourth for "I don't know":

A taxonomy small enough to remember

Map every failure to one of those four classes and "what should the agent do" stops being a per-tool judgment call. Eight codes cover the HTTP reality:

"Retryable" is just shorthand for the transient class — the only one you should ever retry unchanged. Everything else is a bug in the request, a problem with your credentials, or something a human needs to look at.

The hint is the point

Classifying the failure is half of it. The other half is telling the model what to do in words you can paste straight into its context — and the most valuable hint is often the one that tells it to do nothing:

A transient or auth hint that fails to say "don't change the request" is exactly how a good payload gets mangled on the retry. Telling the model what not to touch is as important as telling it what to fix.

Eight lines in your agent loop

This is small enough that you could write it yourself — and everyone does, slightly differently, in every tool. So we pulled it out into agenterr: a dependency-free library that classifies a tool-call result and hands back the code, the class, whether to retry, and the hint.

It's Apache-2.0, has no dependencies, and ships with a language-agnostic spec so ports (Python and TypeScript next) classify identically. It also handles the parts people skip: transport-error timeouts, Retry-After parsing, bounded body snippets in the hint, and a pre-flight SchemaInvalid path so you can catch a bad payload before you even make the call.

What it deliberately doesn't do

agenterr classifies and advises. It never makes the call, retries, backs off, breaks circuits, or persists anything — and it never will. It's a decision function, not an execution engine.

That line matters, because the moment you act on Retryable: true in a way that has to survive a process restart, not block your agent for ten minutes, and leave an audit trail, you've stopped classifying and started building durable execution. That's a different — and much larger — problem. (We wrote up why agents fail at exactly that, five ways over.)

You can build that engine yourself on top of agenterr. Or you can hand the whole thing to klanex, which runs this taxonomy as a service: submit the tool call as an intent and it does the durable retries, per-host circuit breaking, credential sealing, and signed webhooks — returning these exact codes and hints. agenterr is the open taxonomy; klanex is the managed engine that acts on it.

Start with the taxonomy. Add the engine when you need it.

agenterr is open source and free — drop it into your agent loop, or read the spec. When you need the durable execution behind it, klanex is the managed version.