Error Handling
This guide covers common errors, error codes, and how to handle them in agents.
Error Format
All errors follow JSON-RPC 2.0 error format:
json
{
"jsonrpc": "2.0",
"id": 1,
"error": {
"code": -32600,
"message": "Invalid Request",
"data": "decision_id is required"
}
}
Common Error Codes
JSON-RPC Errors
- -32600 Invalid Request - Missing required parameters or malformed request
- -32601 Method not found - Unknown tool name or method
- -32602 Invalid params - Wrong parameter types or values
- -32603 Internal error - Server error
HTTP Status Codes
- 200 OK - Success (even for JSON-RPC errors)
- 401 Unauthorized - Invalid API key
- 403 Forbidden - Permission denied
- 429 Too Many Requests - Rate limited
Common Errors
Invalid API Key
Error:
json
{
"error": {
"code": -32603,
"message": "Internal error",
"data": "Unauthorized"
}
}
HTTP Status: 401
Solution:
- Verify API key is correct
- Check that agent is active
- Ensure API key hasn't been revoked
Missing Required Parameter
Error:
json
{
"error": {
"code": -32602,
"message": "Invalid params",
"data": "decision_id is required"
}
}
Solution:
- Check that all required parameters are provided
- Verify parameter names are correct
- Review tool documentation for required parameters
Invalid Purpose
Error:
json
{
"error": {
"code": -32603,
"message": "Internal error",
"data": "Purpose 'general_access' not in allowed_purposes"
}
}
Solution:
- Check Data Product's allowed purposes
- Use a purpose that's in the allowed list
- Verify purpose spelling is correct
Policy Not Found
Error:
json
{
"error": {
"code": -32603,
"message": "Internal error",
"data": "Policy 'discount_cap_v1' not found"
}
}
Solution:
- Verify policy exists and is published
- Check policy ID spelling
- Ensure policy is accessible to your agent
Data Product Not Found
Error:
json
{
"error": {
"code": -32603,
"message": "Internal error",
"data": "Data Product 'customer_data' not found"
}
}
Solution:
- Verify Data Product exists and is published
- Check Data Product ID spelling
- Ensure Data Product is accessible to your agent
Rate Limited
Error:
json
{
"error": {
"code": -32603,
"message": "Internal error",
"data": "Rate limit exceeded"
}
}
HTTP Status: 429
Solution:
- Check
Retry-Afterheader for wait time - Implement exponential backoff
- Reduce request frequency
Error Handling Patterns
Basic Error Handling
python
def call_tool_safe(client, name, arguments):
"""Call tool with basic error handling."""
try:
return client.call_tool(name, arguments)
except Exception as e:
error_str = str(e)
if "401" in error_str or "Unauthorized" in error_str:
raise Exception("Invalid API key")
elif "403" in error_str or "Forbidden" in error_str:
raise Exception("Permission denied")
elif "429" in error_str or "Rate limit" in error_str:
raise Exception("Rate limited - retry later")
else:
raise
Retry with Backoff
python
import time
import random
def call_tool_with_retry(client, name, arguments, max_retries=3):
"""Call tool with retry and exponential backoff."""
for attempt in range(max_retries):
try:
return client.call_tool(name, arguments)
except Exception as e:
error_str = str(e)
# Don't retry on auth/permission errors
if "401" in error_str or "403" in error_str:
raise
# Retry on rate limits and server errors
if "429" in error_str or "500" in error_str:
if attempt < max_retries - 1:
wait_time = (2 ** attempt) + random.uniform(0, 1)
time.sleep(wait_time)
continue
else:
raise
# Don't retry on other errors
raise
raise Exception("Max retries exceeded")
Decision Error Handling
python
decision_id = None
try:
# Create decision
decision = client.call_tool("decision_create", {...})
decision_id = decision["decision_id"]
# ... perform operations ...
# Commit on success
client.call_tool("decision_close", {
"decision_id": decision_id,
"action": "commit"
})
except Exception as e:
# Rollback on any error
if decision_id:
try:
client.call_tool("decision_close", {
"decision_id": decision_id,
"action": "rollback"
})
except Exception as rollback_error:
# Log rollback error but don't mask original error
print(f"Failed to rollback decision: {rollback_error}")
raise
Idempotency
Use idempotency keys for writes that may retry:
python
import time
idempotency_key = f"order-{order_id}-{int(time.time())}"
try:
write_result = client.call_tool("decision_write", {
"decision_id": decision_id,
"product": "orders",
"purpose": "order_creation",
"mutation": {
"operation": "insert",
"records": [{"order_id": order_id, "total": 1000.00}]
},
"idempotency_key": idempotency_key
})
except Exception as e:
# Retry with same idempotency key
if "retry" in str(e).lower():
write_result = client.call_tool("decision_write", {
"decision_id": decision_id,
"product": "orders",
"purpose": "order_creation",
"mutation": {...},
"idempotency_key": idempotency_key # Same key
})
Failure Modes
SDK-Side Errors
Errors that prevent the envelope from opening:
- Missing intent
- Missing automation_mode
- Missing purpose
Result: Envelope never opens, no trace is created
Runtime Policy Denial
Policy returns deny:
- Envelope transitions to
aborted - Policy evaluation event is recorded
- Decision cannot proceed
Handling:
python
if policy_result["outcome"] == "deny":
client.call_tool("decision_close", {
"decision_id": decision_id,
"action": "rollback"
})
Approval Rejection
Human explicitly rejects:
- Envelope transitions to
rejected - Rejection rationale is recorded
- Decision must abort
Handling:
python
if decision_status["status"] == "rejected":
client.call_tool("decision_close", {
"decision_id": decision_id,
"action": "rollback"
})
Backend Failure
Crash or timeout before commit:
- Envelope transitions to
failed - Partial events are preserved
- No commit occurs
Handling:
python
# Check decision status
decision_status = client.call_tool("decision_get", {
"decision_id": decision_id
})
if decision_status["status"] == "failed":
# Handle failure
# Optionally retry or abort
client.call_tool("decision_close", {
"decision_id": decision_id,
"action": "rollback"
})
Best Practices
- Always handle errors - Don't let errors go unhandled
- Rollback on error - Always rollback decisions on error
- Use idempotency keys - For writes that may retry
- Implement retries - For transient errors (rate limits, server errors)
- Don't retry auth errors - 401/403 errors won't succeed on retry
- Log errors - Log errors for debugging and monitoring