August 23, 2026
Designing API Errors for Agents
My main focus these days is providing agents with the tools to do their jobs: access to information, vendor tools, SaaS APIs. Agent harnesses are constantly improving, but it is clear that a few types of problems tend to trip them up. One of these areas, and in my experience the main source of bugs and wasted tokens, is error handling. Let’s walk through some problems I’ve encountered in practice and tips for better design that should alleviate these issues.
Validate the parameters in stages
One of the first problems you encounter when giving an agent access to an API is that it often passes wrong parameters in the wrong order or makes typos in values it passes. For example, when the agent receives a UUID from one call and needs to pass it verbatim to the next call, there is a big chance it will mangle a part of that identifier. The reason is clear, LLMs are not great at reproducing precise values by design. Of course, humans are not much better, have you ever tried to remember a UUID? Let’s see what we can do to help the agent in this case.
The first thing we do is, unsurprisingly, validation of the input.
- Presence: is the required value supplied?
- Type: string, number, array, or object?
- Syntax: UUID shape, compound-ID segments, allowed characters, length, or enum membership.
- Resolution: does the identifier exist and is it visible in the current connection?
- State: is that resource in a state where this operation is valid?
Each validation stage requires potentially different recovery behavior, so it is not enough to flatten them into one 400 Bad Request. Each root cause should carry its own error code: invalid format, invalid type, resource state conflict etc.
Make it difficult to pass invalid arguments
The best kind of validation is strict self-documenting interface. APIs often require callers to supply randomly generated or opaque identifiers like database keys, compound IDs, long provider-generated strings. It is somewhat manageable in programmatic clients, but confuses agents. Even annotations provided for a request schema don’t guarantee compliance, since the agent can just ignore them when constructing a tool call.
When designing a method prefer parameters the agent can produce reliably. Human-readable names, unique slugs or enum values work better than magic numbers. A single short resource reference is easier to pass than several resource IDs that have to be consistent.
For example, prefer:
{ "site": "engineering", "list": "Incidents" }
or:
{ "site_ref": "site_7b2d" }
over requiring the model to construct:
{
"site_id": "contoso.sharepoint.com,8a0e...-...,e5ad...-..."
}
One major trap I’ve seen is API providers accepting ambiguous references and fuzzy-matching them to the resources. It sort of follows the old design rule that says you should be lenient with your inputs and strict with your outputs. Doesn’t work for agentic clients though, because they can make very random mistakes in the input.
Explain how to correct the error
That’s all part of the classic API design. What else can we do to help specifically agents? Our response text can teach the agent how to fix the problem.
A useful validation response identifies:
- The exact field or JSON path.
- The received and expected type or shape, range, enumeration.
- A correct example when the format is non-obvious.
- The discovery tool to call when the value must come from the system.
- The exact response field to copy, without transformation.
When you can’t avoid opaque identifiers, make them discoverable. Explain to the agent how to retrieve the necessary ID if it escaped its context.
Compare:
API error: 400 Bad Request
with:
Invalid site_id. Expected `hostname,site_uuid,web_uuid`; received two segments.
Call `list_sites` and pass the returned `id` unchanged. Repeating this call with
the same site_id will fail.
The second variant explains the recovery process and prevents the model from inventing a new identifier or spending valuable tokens on inventing an elaborate solution of its own.
For example, list_sites might return:
{
"sites": [
{
"name": "Engineering",
"site_ref": "site_7b2d",
"provider_id": "contoso.sharepoint.com,8a0e...-...,e5ad...-..."
}
],
"usage": "Pass site_ref to other SharePoint tools. Do not construct provider_id."
}
Downstream tools should accept site_ref if possible. If they must accept provider_id, say explicitly: “Use sites[].provider_id returned by list_sites; copy it unchanged.”
Do not relay raw provider errors (unless you need to)
As a general rule, it’s not worth passing the downstream provider error directly to the agent. For one thing, it most likely violates the principles that I described above so will potentially just confuse the agent. It can also expose internal implementation or even secret values to the model’s context. Even worse, it’s a potential source of prompt injection attacks.
On the other hand, some tools provide such a rich surface area of possible operations that mapping them into your error space is impossible. For example if you allow the agent to write a SQL query and run it on a database. The number of possible errors is basically infinite. In this case hiding them from the agent will just be counterproductive and make it try the same query in 1000 different variations.
Errors are part of the tool
If there’s one thing to remember and apply it’s this: error messages, like any other data or content that your API returns to the agent, are now executable. They are part of the implementation of the tool. You need to test them and evaluate how they affect performance of the agent. Design them together with the methods that you provide, so they form single coherent context.
I don't use any web analytics tool to track who reads this blog. If you have some thoughts you'd like to share, please reach out to theelderscripts@gmail.com. I read every email!
Yury
Engineering manager and data engineer. Writing about software engineering, data, AI, and team leadership.
@Heliocene