Tool error messages are prompts, so write them like one
The short answer
Every error string your tools return is read by the model and acted on, so it is prompt engineering whether you meant it or not. "Failed" teaches it nothing and it will guess. A useful error does four things: states the constraint, gives the reason, says what to do instead, and names the specific wrong claim the model must not make. And anything you can check before the call leaves your backend, check there, because a tool description is advice and validation is binding.
In ordinary software an error message is written for a developer reading a log. In an agent, the first reader is the model, and the model does not file a bug. It acts on what the message says, immediately, in front of the user. That makes every error string your tools return a prompt, whether you wrote it as one or not.
What “failed” teaches a model
Nothing, so it guesses. Return {"error": "failed"} and the model will reshape the arguments and try again, or invent a reason and tell the user that reason as though it knew. Neither is malicious. It has been handed a gap and asked to be helpful, and a plausible explanation is the most helpful-looking thing it can produce.
The four parts of an error a model can act on
Here is the shape of an error from one production agent, returned when a disabled tool was called:
This tool is disabled. Writing to the editor is done by the user, not by you, because a failed write used to report success and cost a user their work. Show the code and give the short manual steps. Do not claim anything was written, applied or loaded.
It does four separate jobs:
- States the constraint. The tool is disabled.
- Gives the reason. Without it the model fills the gap with its own.
- Prescribes the alternative. Show the code and the manual steps.
- Blocks the specific lie. The likeliest wrong outcome is a cheerful summary saying it was done, so the message forbids exactly that.
The last one looks odd until you have watched a model summarise a refused action as a completed one. After that it looks like the most important line.
Distinguish the cases a user would act on differently
The same agent separated “not available in this mode” from “not available anywhere”, and a comment in the code says why: otherwise the model invents its own explanation. Telling a user to switch modes when no mode can do the thing sends them hunting for a setting that does not exist.
The general rule: if two failures need the user to do different things, they need different messages. A missing precondition is the clearest example. A tool that needs data loaded first is not broken, it is waiting, and its error should name the step that fixes it rather than read like a failure worth retrying.
Descriptions are advisory. Validation is binding.
It is tempting to put every rule in the tool description and trust it. In that production agent the description stated how many anchor points a shape required, and the model sent the wrong number on the very next run. The call went out, the external service rejected it, and a full round trip was spent learning something the backend could have checked in a microsecond.
Anything checkable locally, check locally, before the call leaves your code. It is faster, it is cheaper, and the error text is yours to write, which means you can write it with all four parts above instead of relaying whatever the downstream API chose to say.
Name the missing thing, not the symptom
The worst errors describe what went wrong without saying where. An agent integration where the customer forgot to register a function should not report “tool failed”; it should name the call they have not made. The first sends a developer to debug the model. The second sends them to the one line they are missing.
Errors that pass through to the user are covered in why agents report failures as success, which is the failure these messages exist to prevent.
Common questions
Should tool errors be short?
Short is fine, vague is not. One sentence that says what is wrong and what to do instead is worth more than a stack trace, and far more than a bare "error", which leaves the model to invent an explanation and present it as fact.
Isn't putting the rule in the tool description enough?
No. Descriptions are advisory. In one production agent the description stated how many points a shape needed and the model sent the wrong number on the very next run. Validate the arguments in your own code before the call goes out, and return an error that says what to send instead.
Should errors ever tell the model what not to say?
Yes, when there is a specific lie it is likely to tell. If a write was refused, the error should say not to claim anything was saved. Models are trying to be helpful, and a helpful summary of a failed action is exactly how users end up believing it worked.
Keep reading
- Why your AI agent says an action worked when it didn't
The 200 that lied: why a successful response is not evidence, and the per-tool success shape that stops an agent claiming work it never did.
- How to stop an AI agent retrying a failing tool forever
The 23-call spiral, why per-tool caps let a model walk the tool list, and budgeting failures by cause.
- How to test an AI agent that takes actions, without touching real data
Run it for real against development data, the five cases worth deliberately breaking, and why a timer on real execution backfires.
Verb is this, built. An AI assistant you embed in your SaaS with one script tag: it calls your own API as the signed-in user, confirms before it changes anything, and logs every action. Free to build and test.