Error Handling¶
Overview¶
Tools fail. Networks fail. Permissions fail. Building resilient systems means handling every failure gracefully.
4 Error Recovery Strategies¶
1. Retry with Backoff¶
def call_with_retry(tool, args, max_retries=3):
for attempt in range(max_retries):
try:
return tool.call(**args)
except TransientError:
if attempt < max_retries - 1:
wait_time = 2 ** attempt # Exponential backoff
time.sleep(wait_time)
else:
raise
2. Graceful Degradation¶
def call_with_fallback(primary_tool, fallback_tool, args):
try:
return primary_tool.call(**args)
except Exception as e:
return fallback_tool.call(**args)
3. Circuit Breaker¶
class CircuitBreaker:
def __init__(self, failure_threshold=5):
self.failure_count = 0
self.threshold = failure_threshold
self.is_open = False
def call(self, tool, args):
if self.is_open:
raise CircuitBreakerOpen("Tool temporarily unavailable")
try:
result = tool.call(**args)
self.failure_count = 0
return result
except Exception as e:
self.failure_count += 1
if self.failure_count >= self.threshold:
self.is_open = True
raise
4. Timeout Handling¶
import signal
def call_with_timeout(tool, args, timeout_sec=30):
def timeout_handler(signum, frame):
raise TimeoutError(f"Tool exceeded {timeout_sec}s")
signal.signal(signal.SIGALRM, timeout_handler)
signal.alarm(timeout_sec)
try:
result = tool.call(**args)
signal.alarm(0) # Cancel alarm
return result
except TimeoutError:
return {"error": f"Timeout after {timeout_sec}s"}
Error Classification¶
| Type | Behavior | Recovery |
|---|---|---|
| Transient | Temporary (network timeout) | Retry with backoff |
| Permanent | Won't fix by retrying (permission) | Fallback or escalate |
| Timeout | Takes too long | Use fallback |
| Rate Limit | Too many requests | Wait and retry |
Best Practices¶
- Always validate inputs before calling tools
- Log all errors for debugging
- Monitor failure rates to detect issues
- Use exponential backoff for retries
- Set reasonable timeouts
- Provide clear error messages to agents
-
Last Updated: August 9, 2026