Errors are tool_result content
When a tool fails, do not raise the exception out of the loop — return a tool_result with an error payload. Claude can read 'database query failed: timeout' and decide to retry, ask the user, or pivot. Hiding errors makes the model blind.
The is_error flag
Tool result blocks support is_error: True. Use it to flag programmatic errors so the model knows the difference between a successful query that returned 'no rows' and a query that failed. Both have content; only one is an error.
Retry budgets per tool
Some tools (HTTP fetches, database queries) deserve a retry on transient errors before reporting back to the model. Others (writes with side effects) should fail fast. Bake the policy into the handler, not the loop — the loop should be tool-agnostic.