Building AI Agents from Scratch
Handling Tool Failures
Timeouts, bad parameters, API errors, retries, and letting the agent recover inside the loop.
Tools fail. If you raise into the loop, the process dies. If you return a stack trace, the model apologizes forever. Return a small JSON error and let the next think choose: retry, other tool, or tell the user.
Shape every failure
import json
import time
def run_tool(name: str, arguments_json: str, retries: int = 1) -> str:
fn = REGISTRY.get(name)
if fn is None:
return json.dumps({"ok": False, "error": "unknown tool", "name": name})
try:
args = json.loads(arguments_json or "{}")
except json.JSONDecodeError:
return json.dumps({"ok": False, "error": "arguments are not JSON"})
last = {"ok": False, "error": "not attempted"}
for attempt in range(retries + 1):
try:
last = fn(**args) if isinstance(args, dict) else {"ok": False, "error": "args must be an object"}
if last.get("ok") or last.get("error") != "timeout":
return json.dumps(last)
except TimeoutError:
last = {"ok": False, "error": "timeout", "attempt": attempt + 1}
time.sleep(0.5 * (attempt + 1))
except TypeError as exc:
return json.dumps({"ok": False, "error": "bad parameters", "detail": str(exc)})
except Exception as exc:
return json.dumps({"ok": False, "error": type(exc).__name__, "detail": str(exc)[:200]})
return json.dumps(last)Retry only the failures that are transient (timeout, 429, 503). Do not retry “path escapes the project” or “unknown tool.” Those need a different argument or a different tool.
Tell the model what to do
System: “If a tool returns ok false, do not pretend it worked. Change arguments, pick another tool, or report the error. One retry is enough unless the user asks to keep trying.”
Check it
- Pass a string where a path is required —
bad parameters, no traceback on stderr. - Force a timeout in
http_get(a tiny timeout against a slow host). You should seeattemptincrement, then a final error the model quotes. - Unknown tool name in a hand-built call — the registry answers; the loop continues.
Next: make the final answer a JSON object your app can store.