Make Tools Fail in a Way Claude Can Recover From
What it is
A design rule for tools Claude calls: when a tool fails, return an error the model can act on — what went wrong, why, and what a valid call would look like — instead of a raw stack trace or a bare status code. The error message is part of the prompt Claude reads next.
Why it works
In an agent loop, a tool result feeds straight back into Claude's next decision. A message like '500 Internal Error' gives it nothing to correct, so it retries blindly or gives up. A message like 'date must be YYYY-MM-DD; you sent 03/14/2025' tells Claude exactly how to fix the call, and it usually does on the next turn. Treating errors as instructions — not just as failures to report — is what turns a brittle tool chain into one that self-corrects.
When to use it
Any function-calling or agent setup where Claude makes multiple tool calls and needs to adapt: API wrappers, database queries, file operations, anything with validation or rate limits. Especially when you've seen the model loop on a failing call.
When not to use it
One-shot tools that either succeed or hand control straight back to a human — if there's no next model turn to consume the error, rich phrasing for the model is wasted effort. Still log the detail for yourself.
Prompt
Design the error responses for this tool before the happy path. For each failure mode (bad input, not found, rate limited, unauthorised), return a short message that states: (1) what was wrong, (2) why, (3) a concrete example of a valid call. Never return a raw stack trace to the model. Here is the tool: <spec>.Example
A search_orders tool used to return {"error": true} on a malformed filter, and Claude would retry the identical call in a loop. Rewritten, it returns "status must be one of: open, shipped, cancelled — you sent 'complete'". On the next turn Claude sends status: shipped and the loop resolves in one step instead of five.
Advanced version
Distinguish retryable from terminal failures explicitly — mark rate limits and transient network errors as 'retry after N seconds' and mark bad-input or not-found as 'do not retry, change the call.' Claude can then back off on the former and correct on the latter, instead of treating every failure the same way.
Common mistakes
- Leaking raw exceptions or full stack traces into the tool result — noise the model can't use and a potential info-leak.
- Returning success-shaped output on partial failure, so Claude proceeds on data that isn't actually there.
- Making every error look identical, so the model can't tell 'retry' from 'fix your input' from 'stop'.