When a language model calls a tool, the result comes back to it as an observation, alongside the full prior conversation (race and observation loop). The model has no debugger and no logs. The string you return is the entire world. A message of "Error: failed" tells the model that something broke but not what, so its only available move is to try again, hoping the transient condition clears. Raw stack traces are worse: they are long, full of frame names the model cannot act on, and consume tokens on every turn.
This is the same problem technical writers have solved for decades in other directions. Classic error-message guidance says to show the error close to its source and make it constructive, so the reader can act (error-message guidelines). API design guidance is blunt about it: an error exists so that both humans and software can decide what to do next, and it should carry an application-specific code that clients can branch on (errors best practices). If you treat the model as a client rather than a person, those rules transfer almost unchanged.
There is a second, subtler cause. Ambiguous tool feedback creates loops. A tool that returns a partial result, or adds a phrase like "more results may be available", signals to the agent that another call might produce something better, so it calls again with the same arguments. In a published demonstration of this pattern, ambiguous tools produced fourteen calls before the agent gave up, while the same tasks with clear success states stopped in two (reasoning-loop experiment). The extra twelve calls were pure spend. The model was doing what the text implied.
The lesson is uncomfortable but simple: the error message is an API design surface, and it is usually the only error surface the model ever sees. Documentation teams already know that incomplete or misleading descriptions push consumers into support tickets and failed integrations (documentation's effect on adoption). For an agent, "support ticket" means another tool call, billed to you.



