An agent that calls a tool receives a response body and must decide what to do next without leaving the execution loop. That makes the error payload a specification, not a message. A classic 400 Bad Request with a body of {"error": "invalid input"} works for a human, who can read the docs, see the red UI field and try again manually, but an agent has none of those fallbacks. It receives the response, interprets it and must act, all inside the loop, as laid out in Error Handling for AI Agents: A Different Standard Than Classic Apps.
The core principle follows from that: every error must tell the agent what to do next. Error design shifts from reactive (describing what went wrong) to prescriptive (describing what to try instead), and the message becomes documentation read at runtime.
A worked example of the same failure redesigned for an agent, from that same analysis:
{
"error": "Invalid value for field 'status'",
"received": "done",
"allowed_values": ["pending", "in_progress", "completed", "cancelled"],
"hint": "Did you mean 'completed' instead of 'done'?"
}
The first version tells the agent that something failed. The second tells it which field failed, what arrived, what was acceptable and how to fix the call. Only the second supports automatic recovery.
There is a second reason the contract matters. Tool errors compound in multi-step agents: a wrong argument in step two corrupts the inputs to step three, which corrupts step four, until the task collapses. Small improvements at the tool boundary therefore have outsized downstream effects on completion rates, according to Designing Tools for LLM Agents. The contract bounds the blast radius of each failure.



