Handle tool errors in agents

In a normal program an error stops the code. In an agent it becomes part of the conversation, and the model decides what to do next. Give it errors it can act on, keep the ones it cannot fix away from it, and put hard limits on the loop.

An agent was asked to define "idempotent" for a glossary. The dictionary tool returned a 404. The model, seeing an error it did not understand, tried again with "Idempotent", then "idempotency", then "idempotent (computing)", then went back to the original spelling. Each call was a few credits; the loop had no ceiling; the user's tab had long since closed.

Nothing in that story is a bug in the API or the model. The tool returned an accurate answer ("no entry for that word") in a form the model could not interpret, and the loop around it had no way to stop. Both of those are your code, and both are fixable.

Errors are now part of the conversation

In the Fundamentals courses, an error from an API went to your code and your code decided: retry, give up, show a message. In an agent there is a second decider. Whatever your tool returns goes back to the model as a tool result, and the model chooses the next step: try other arguments, try another tool, ask the user, or answer without the data.

That gives you a design choice for every failure: does the model need to know about this, and can it do anything useful with it?

  • Some failures are about the input, and the model is the one who chose the input. It should hear about them, in words that point at a fix.
  • Some failures are transient. The model cannot do anything about a busy minute, so your tool layer should absorb them with a bounded retry and only report if that fails.
  • Some failures are fatal for the whole run: a bad key, an empty credit balance. The model cannot fix those either, and telling it invites it to try. Your loop should stop and tell a human.

Model APIs give you a channel for the first group. In the Anthropic Messages API a tool_result can carry is_error: true; in OpenAI's API you return the error text as the tool message content. MCP servers do the same thing: the APIVerve MCP server returns a failed call as tool content with isError: true and the API's own message, rather than a protocol error, precisely so the agent can read it and correct itself.

Map every status to a decision

Here is how APIVerve's statuses translate into agent behaviour. The first column is what the API says; the last is what the model should see.

StatusWhat it meansWho handles itWhat the model sees
400Invalid or missing parameterModelThe API's message plus "fix the argument"
404Unknown API, or no result for this inputModel"No result for X. Do not retry the same input."
401Missing or unrecognised keyYour loop: haltNothing; the run stops
403Valid key, but not allowed for this call (scoping, IP allow-list)Your loop: disable the tool"This tool is unavailable; answer without it"
429 + credits leftPer-minute rate limitTool layer: wait retry-after, retry once or twiceOnly if retries fail: "temporarily unavailable"
429 + x-api-remaining-credits: 0Monthly credits exhaustedYour loop: haltNothing; the run stops
5xx, timeout, networkUpstream troubleTool layer: one retryIf still failing: "temporarily unavailable"

Three rows deserve a closer look.

404 is often an answer, not a failure. The dictionary returns 404 with "word not found" when it has no entry, and World Time returns 404 with "city not found". That is information: the input was understood and nothing matched. The right model behaviour is to stop asking about that input, tell the user, or try a meaningfully different input (a larger nearby city). Retrying the same string is never right.

Try it liveDictionary2 credits
GEThttps://api.apiverve.com/v1/dictionary
curl "https://api.apiverve.com/v1/dictionary?word=retry" \
  -H "x-api-key: YOUR_API_KEY"
Sign in or create a free account to run this with your own key.

A hit returns definitions and definitionCount. Try a technical term such as idempotent: it returns 404, word not found.

When we tried idempotent, the dictionary returned 404. The word is real; this dictionary has no entry for it. Your error text should say exactly that, "no entry in the dictionary", not "invalid word", or the model will tell the user the word does not exist.

Both 429s look the same until you read a header. A rate-limit 429 clears in seconds. A credits 429 does not clear until the billing cycle renews. Check x-api-remaining-credits: if it is 0, no amount of waiting or retrying helps, and every retry is a wasted request. Halt the run and surface it.

403 is about the key, not the request. If you scoped the agent's sub-key to block an endpoint, a 403 is that scoping working. The model should drop the tool for the rest of the conversation. Sending the user to sign in again or retrying are both wrong.

Check yourself

Inside an agent run, a tool call returns 429 and the response header x-api-remaining-credits is 0. What should happen?

Answer the check above to continue.

Run every example on your own key. Free plan, no card.

Scaling up?

Volume pricing, custom SLAs, and dedicated support for high-traffic teams.

Contact sales