Error hierarchy
All Agent Sentinel errors inherit from AgentSentinelError:
from agent_sentinel import (
AgentSentinelError,
BudgetExceededError,
PolicyViolationError,
ReplayDivergenceError,
NetworkError,
SyncError,
TimeoutError,
ConfigurationError,
)
Error structure
Every error includes:
- Message: Human-readable description
- Error code: Machine-readable identifier
- Details: Additional context (dict)
- Recoverable flag: Whether the operation can be retried
try:
# Some operation
...
except AgentSentinelError as e:
print(f"Error: {e.message}")
print(f"Code: {e.error_code}")
print(f"Details: {e.details}")
print(f"Recoverable: {e.recoverable}")
# Serialize for logging
error_dict = e.to_dict()
BudgetExceededError
Raised when cost limits are exceeded:
from agent_sentinel import BudgetExceededError, guarded_action, PolicyEngine
PolicyEngine.configure(run_budget=1.0)
@guarded_action(name="expensive_call", cost_usd=0.60)
def call_api():
return "result"
try:
call_api() # First call: $0.60 spent
call_api() # Second call: Would exceed $1.00 budget → raises BudgetExceededError
except BudgetExceededError as e:
print(f"Budget exceeded: spent ${e.details['spent']}, limit ${e.details['limit']}")
print(f"Budget type: {e.details['budget_type']}") # "run", "session", or "action"
# Graceful degradation
if e.details['budget_type'] == 'action':
# Switch to cheaper model
use_cheaper_model()
elif e.details['budget_type'] == 'run':
# Stop the run
terminate_run()
Details
spent (float): Current spend amount
limit (float): Budget limit
budget_type (str): “session”, “run”, or “action”
- Recoverable:
False (budget is truly exceeded)
PolicyViolationError
Raised when an action violates policy rules:
from agent_sentinel import PolicyViolationError, guarded_action, PolicyEngine
PolicyEngine.configure(
denied_actions=["delete_production_db"],
allowed_actions=["read_db", "write_db"], # Allowlist mode
)
@guarded_action(name="delete_production_db")
def dangerous_action():
return "never executed"
try:
dangerous_action()
except PolicyViolationError as e:
print(f"Policy violation: {e.message}")
print(f"Rule: {e.details['policy_rule']}") # "denied_action" or "allowlist" or "rate_limit"
print(f"Action: {e.details['action_name']}")
# Log security incident
log_security_event(
action=e.details['action_name'],
rule=e.details['policy_rule'],
agent_id=get_agent_id()
)
Details
policy_rule (str): Which rule was violated (“denied_action”, “allowlist”, “rate_limit”)
action_name (str): Name of the blocked action
- Recoverable:
False (policy is enforced)
ReplayDivergenceError
Raised when replay mode detects non-deterministic behavior:
from agent_sentinel import ReplayDivergenceError, replay_mode
try:
with replay_mode(run_id="run-123", strict=True):
# If agent behavior diverges from recording, raises ReplayDivergenceError
run_agent()
except ReplayDivergenceError as e:
print(f"Divergence detected: {e.message}")
print(f"Expected: {e.details['expected']}")
print(f"Actual: {e.details['actual']}")
print(f"Action: {e.details['action_name']}")
# Analyze divergence
analyze_non_determinism(e.details)
Details
action_name (str): Action where divergence occurred
expected (any): Expected value from recording
actual (any): Actual value from replay
- Recoverable:
False (indicates non-determinism)
NetworkError
Raised on HTTP/network failures when communicating with platform:
from agent_sentinel import NetworkError, enable_remote_sync
try:
enable_remote_sync(
platform_url="https://platform.agentsentinel.dev",
api_token="as_invalid_token",
run_id="run-123"
)
except NetworkError as e:
print(f"Network error: {e.message}")
print(f"Status code: {e.details['status_code']}")
print(f"Endpoint: {e.details['endpoint']}")
print(f"Recoverable: {e.recoverable}") # True for 5xx, False for 4xx
if e.recoverable:
# Retry with backoff
retry_with_backoff(enable_remote_sync, ...)
else:
# Authentication or configuration issue - don't retry
log_error("Invalid API token or endpoint")
Details
status_code (int): HTTP status code
endpoint (str): API endpoint that failed
- Recoverable:
True for 5xx (server errors), False for 4xx (client errors)
SyncError
Raised when background sync fails to upload telemetry:
from agent_sentinel import SyncError
try:
# Sync errors are typically raised internally and logged
# but you can handle them if using manual flush
sync_service.flush_now()
except SyncError as e:
print(f"Sync failed: {e.message}")
print(f"Batch size: {e.details['batch_size']}")
print(f"Retry count: {e.details['retry_count']}")
print(f"Recoverable: {e.recoverable}") # Always True
# Background sync will automatically retry
# No action needed - just log for visibility
log_warning(f"Sync retry {e.details['retry_count']}")
Details
batch_size (int): Number of records that failed to sync
retry_count (int): Current retry attempt
- Recoverable:
True (sync will be retried automatically)
TimeoutError
Raised when an operation exceeds timeout:
from agent_sentinel import TimeoutError, ApprovalClient
client = ApprovalClient(
platform_url="https://platform.agentsentinel.dev",
api_token="as_your_api_key_here",
)
try:
response = client.request_approval_sync(
action_name="expensive_operation",
timeout_seconds=60, # 1 minute timeout
# ...
)
except TimeoutError as e:
print(f"Timeout: {e.message}")
print(f"Operation: {e.details['operation']}")
print(f"Timeout: {e.details['timeout_seconds']}s")
print(f"Recoverable: {e.recoverable}") # True
# Handle timeout gracefully
if e.details['operation'] == 'approval':
# Maybe extend timeout or proceed without approval
log_warning("Approval timed out - using default policy")
Details
timeout_seconds (int): Timeout duration
operation (str): What timed out (e.g., “approval”, “policy_sync”)
- Recoverable:
True (can retry with longer timeout)
ConfigurationError
Raised on invalid configuration:
from agent_sentinel import ConfigurationError, PolicyEngine
try:
PolicyEngine.configure(
session_budget=-10.0, # Invalid: negative budget
)
except ConfigurationError as e:
print(f"Configuration error: {e.message}")
print(f"Config key: {e.details['config_key']}")
print(f"Invalid value: {e.details.get('value')}")
print(f"Recoverable: {e.recoverable}") # False
# Fix configuration
PolicyEngine.configure(session_budget=10.0)
Details
config_key (str): Configuration parameter that’s invalid
value (any): Invalid value (if applicable)
- Recoverable:
False (must fix configuration)
Handling errors gracefully
Pattern: Graceful degradation
from agent_sentinel import BudgetExceededError, guarded_action
@guarded_action(name="premium_llm", cost_usd=0.50)
def call_premium_llm(prompt):
return premium_model.generate(prompt)
@guarded_action(name="cheap_llm", cost_usd=0.01)
def call_cheap_llm(prompt):
return cheap_model.generate(prompt)
def generate_with_fallback(prompt):
try:
return call_premium_llm(prompt)
except BudgetExceededError:
# Degrade to cheaper model
logger.warning("Budget exceeded, using cheap model")
return call_cheap_llm(prompt)
Pattern: Retry with backoff
from agent_sentinel import NetworkError
import time
def call_with_retry(func, max_retries=3):
for attempt in range(max_retries):
try:
return func()
except NetworkError as e:
if not e.recoverable or attempt == max_retries - 1:
raise
# Exponential backoff
wait = (2 ** attempt) + (random.random() * 0.1)
time.sleep(wait)
Pattern: Error monitoring
from agent_sentinel import AgentSentinelError
def monitor_errors(func):
def wrapper(*args, **kwargs):
try:
return func(*args, **kwargs)
except AgentSentinelError as e:
# Send to monitoring service
monitoring.track_error(
error_code=e.error_code,
message=e.message,
details=e.details,
recoverable=e.recoverable,
)
raise
return wrapper
Best practices
Always handle BudgetExceededError: Implement graceful degradation (cheaper models, reduced scope) rather than crashing.
Log PolicyViolationError as security events: These indicate attempted policy violations and should be monitored.
Don’t retry non-recoverable errors: Check error.recoverable before retrying. Configuration errors and policy violations won’t succeed on retry.
Use error details for context: Error details contain rich context - use them for debugging, alerting, and graceful degradation logic.
See also