Map every tool error to a response, or your agent guesses

4xx, 5xx, a soft decline in a 200 body: each one means something different and demands a different move. An agent with no error taxonomy treats them all as either success or a blind retry.

B

Balagei G Nagarajan

5 MIN READ


Agents routinely read a non-success response as success.A comprehensive study of bugs in LLM agents finds tool errors, warnings, and non-2xx codes parsed as completion, so the next step runs on a failure (When Agents Fail, arXiv:2601.15232, 2025). A 200 with an error nested inside is the trap your agent walks straight into.

A control panel of status lights where one green light hides a small red fault inside it, while nearby a router sends different colored signals down clearly separate, correctly labeled paths
Define the taxonomy once: 4xx fix-or-abort, 5xx bounded idempotent retry, soft-fail escalate, success proceed.
— from “Map every tool error to a response, or your agent guesses”

Key facts.

  • Agents append the tool observation and keep reasoning, so a non-success status or an error nested in a 200 body is read as ordinary data unless something parses it; PALADIN catalogs API exceptions, malformed outputs, and partial execution among the failure modes (PALADIN, arXiv:2509.25238, 2026).
  • A comprehensive study of LLM-agent bugs finds error responses and warnings misclassified as success and propagating silently downstream (When Agents Fail, arXiv:2601.15232, 2025).
  • HTTP status classes have defined meanings, a 4xx is a client error to fix and a 5xx a server fault that may be retried, so the response class should decide the next move (RFC 9110, HTTP Semantics), and a non-idempotent call must never be blind-retried.
  • With no error taxonomy a 200 soft-decline reads as success, and a stronger model collapses it the same way. (arXiv:2601.15232)

Why status-code handling is where agents fail

The agent treats the whole response as text to reason over, not a typed result with a status to branch on. In a normal loop the tool output gets concatenated into the conversation and the model continues. If nothing parses the status, a 4xx, a 5xx, a warning, or a declined state buried in a 200 body is just more prose, and the model, trained on the happy path, reads it as success. The worst case is the plausible one: a 200 OK wrapping a nested declined or error state. The agent confidently treats it as done and then confirms an order, updates inventory, or emails a customer on a transaction that never cleared.

What an error taxonomy gives you

A defined move for every failure class instead of a blind reaction. A 4xx: the request was wrong, retrying unchanged is pointless, fix the args or stop. A 5xx: usually transient, a bounded retry with backoff is right, but only if the call is idempotent. A soft decline or warning in a success body: don't proceed, escalate. Mapping each class to retry, fix, escalate, or abort turns the response into a decision. It stops the agent from either ignoring failures or hammering a doomed call.

# Status class -> defined action, not a blind retry
def handle(resp):
 if resp.status >= 500: return RETRY_WITH_BACKOFF if idempotent else ESCALATE
 if resp.status >= 400: return FIX_ARGS_OR_ABORT # retrying unchanged won't help
 if resp.body.get("state") in ("declined","error"): return ESCALATE # soft fail in a 200
 return PROCEED

Building it into the agent

Validate the full response, status code and body, before the result reaches the model. Attach a typed outcome the runtime can branch on. Define the taxonomy once: 4xx fix-or-abort, 5xx bounded idempotent retry, soft-fail escalate, success proceed. Apply it at every tool boundary. Don't auto-retry a mutating call without an idempotency key. Log the full raw response so failures are diagnosable. Fault-inject these conditions in testing so the happy-path bias shows up before production. Then the agent backs off on a 5xx, fixes or stops on a 4xx, and escalates on a soft decline, each on purpose, not by accident.

A decision matrix mapping response classes, 2xx clean, 200-with-error, 4xx, 5xx, to defined actions, proceed, escalate, fix or abort, retry with backoff, with idempotency noted as a gate on retry

Status class to action

ResponseWhat it meansCorrect action
2xx, clean bodyReal successProceed
200 with error/declined bodySoft failureEscalate, do not proceed
4xxRequest was wrongFix arguments or abort, do not blind-retry
5xxTransient server faultBounded retry with backoff, if idempotent
Warning header / partialDegraded resultInspect, validate completeness

The pattern is that a tool response is a typed outcome, and an agent that reads it as prose either trusts a failure or retries one blindly. Give every status class a defined action, validate the body as well as the code, and gate retries on idempotency, and the agent answers each failure correctly instead of guessing. That disciplined handling of the tool boundary, every outcome mapped to a move, is reliability at the pattern level, which is what VibeModel builds as the Pattern Intelligence Layer.

Frequently asked questions

Why is a 200 with an error body the worst case?
Because the status line says success and the agent believes it, so the failure in the body slips through and the next step runs on a transaction that never completed. Validate the body, not just the code.

when's it safe to auto-retry?
On transient 5xx faults, and only when the call is idempotent so a retry can't duplicate the action. Never auto-retry a 4xx unchanged, and never auto-retry a mutating call without an idempotency key.

Where should the taxonomy live?
At the tool boundary, in the runtime, before the result reaches the model. The model should receive a typed outcome to branch on, not a raw response to interpret.


Share this post

Join the discussion

Have a take, a war story, or a question? Sign in with GitHub to comment and react. Comments are powered by GitHub Discussions, ad-free and yours to moderate.

Continue Reading

Find where your agent breaks, before you build it

Faultmap maps where your agent will fail from the goal and your data, then hands you the first test suite it has to pass.