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_modetry: 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_synctry: enable_remote_sync( 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 SyncErrortry: # 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, ApprovalClientclient = ApprovalClient( 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”)
Raised under the default fail_closed_writes enforcement mode (SDK 0.4.0+) when
no policy can vouch for an action declared impact="medium" or "high". It is a
PolicyViolationError subclass with reason code NO_POLICY_FOR_WRITE.
from agent_sentinel import NoPolicyForWriteErrortry: issue_refund("ord_123", 40.0) # impact="high", no policy loaded yetexcept NoPolicyForWriteError as e: print(e.error_code) # NO_POLICY_FOR_WRITE
Fix it by loading a policy (in code, YAML or remote sync),
or by declaring genuinely read-only tools impact="read". See
Enforcement modes.
PolicyNotConfiguredError
Raised under strict fail_closed when no policy is loaded, whatever the declared
impact. Also a PolicyViolationError subclass.
AgentKilledError and KillSwitchUnavailableError
AgentKilledError (code AGENT_KILLED, not recoverable) is raised before an
action runs when its agent, run or mission has an active
kill switch. details carries target_type and
target_id.KillSwitchUnavailableError subclasses it. It is raised only under strict
fail_closed, when the kill-switch check itself cannot complete. An agent that
can’t be confirmed as not killed is treated as killed, so existing
except AgentKilledError handlers catch it. details["reason_code"] is
KILL_SWITCH_UNAVAILABLE.
from agent_sentinel import AgentKilledError, KillSwitchUnavailableErrortry: run_step()except KillSwitchUnavailableError: # platform unreachable under fail_closed; retry later or stop ...except AgentKilledError as e: # an operator killed this target; stop the run print(e.details["target_type"], e.details["target_id"])
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 NetworkErrorimport timedef 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 AgentSentinelErrordef 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.