# Error Handling ## Overview The `cleveragents.core.error_handling` module provides centralized error classification, secret redaction, and exception wrapping for consistent CLI output and safe logging. Secret redaction **delegates** to `cleveragents.shared.redaction` so that all redaction paths (structlog processors, CLI output, error details) share the same patterns and configuration. On import, the module registers additional patterns (JWT, GitHub PAT, GitLab PAT) with the shared redaction registry. ## CLI Integration The top-level CLI error handlers in `cli/main.py` use `classify_error` and `wrap_unexpected` to ensure every exception surfaced to the user is classified with an HTTP-like error code and has secrets redacted from both the message and any attached details. ```python except CleverAgentsError as e: from cleveragents.core.error_handling import classify_error info = classify_error(e) print(f"Error [{info.code.value}] {info.code.name}: {info.message}") except Exception as e: from cleveragents.core.error_handling import classify_error, wrap_unexpected safe = wrap_unexpected(e) info = classify_error(safe) print(f"Error [{info.code.value}] {info.code.name}: {info.message}") ``` ## Error Codes All errors are mapped to HTTP-like numeric codes: ### Client Errors (400-range) | Code | Name | Mapped From | |------|---------------------|-------------------------------| | 400 | BAD_REQUEST | BusinessRuleViolation, PlanError, DomainError | | 401 | UNAUTHORIZED | AuthenticationError | | 403 | FORBIDDEN | AuthorizationError | | 404 | NOT_FOUND | ResourceNotFoundError | | 409 | CONFLICT | ResourceConflictError | | 422 | VALIDATION_FAILED | ValidationError | | 429 | RATE_LIMITED | RateLimitError | ### Server Errors (500-range) | Code | Name | Mapped From | |------|---------------------|-------------------------------| | 500 | INTERNAL | InfrastructureError, CleverAgentsError (catch-all) | | 503 | SERVICE_UNAVAILABLE | ExternalServiceError | | 520 | PROVIDER_ERROR | ProviderError | | 521 | EXECUTION_ERROR | ExecutionError | | 522 | DATABASE_ERROR | DatabaseError | | 523 | FILESYSTEM_ERROR | FileSystemError | | 524 | NETWORK_ERROR | NetworkError | | 525 | CONFIGURATION_ERROR | ConfigurationError | | 526 | TOKEN_LIMIT | TokenLimitExceededError | | 527 | MODEL_UNAVAILABLE | ModelNotAvailableError | | 528 | STREAM_ERROR | StreamRoutingError | ## Error Categories Each `ErrorInfo` includes a `category` field derived from the error code: | Value | Name | Code Range | Description | |-------|--------|------------|--------------------------| | 4 | CLIENT | 400-499 | Client / input errors | | 5 | SERVER | 500-599 | Server / infrastructure | ```python from cleveragents.core.error_handling import classify_error, ErrorCategory info = classify_error(exc) if info.category == ErrorCategory.CLIENT: print("User-correctable error") ``` ## Secret Redaction Redaction is handled by `cleveragents.shared.redaction` (the single source of truth for all secret masking). The `redact_error_details` function in this module is a thin wrapper around `redact_dict` that forces `show_secrets=False` so error details are **always** masked. ### Redacted Keys (via `shared.redaction`) Key names are matched by **substring** against the shared sensitive key list: `api_key`, `apikey`, `password`, `passwd`, `secret`, `token`, `credential`, `private_key`, `access_key`, `auth`. False positives like `token_count` and `max_tokens` are excluded. ### Pattern-Based Redaction String values are scanned for secret patterns from the shared registry plus the additional patterns registered by this module: - OpenAI API keys (`sk-...`, `sk-proj-...`) - Anthropic keys (`sk-ant-...`) - JWT tokens (`eyJ...`) - GitHub PATs (`ghp_...`, `ghs_...`) - GitLab PATs (`glpat-...`) - Bearer tokens - Token IDs (`tok_...`) ### Single-Value Redaction The `redact_value` function (re-exported from `cleveragents.shared.redaction`) scans a single string for secret patterns and replaces matches: ```python from cleveragents.core.error_handling import redact_value safe = redact_value("token is sk-abcdefghijklmnopqrstuvwxyz") # => "token is ***REDACTED***" ``` This is the same function used internally by `classify_error` to redact exception messages before they reach CLI output. ### Dict Redaction ```python from cleveragents.core.error_handling import redact_error_details safe = redact_error_details({ "api_key": "sk-real-secret", "plan_id": "01PLAN001", }) # => {"api_key": "***REDACTED***", "plan_id": "01PLAN001"} ``` ## Wrapping Unexpected Exceptions Use `wrap_unexpected` to convert non-`CleverAgentsError` exceptions into safe, user-facing errors: ```python from cleveragents.core.error_handling import wrap_unexpected try: risky_operation() except Exception as exc: safe = wrap_unexpected(exc, context={"plan_id": plan_id}) raise safe from exc ``` If the exception is already a `CleverAgentsError`, it is returned with optional context merged into a **copy** of its details (the original details dict is not mutated). ## CLI Formatting ```python from cleveragents.core.error_handling import classify_error, format_error_for_cli info = classify_error(exc) print(format_error_for_cli(info)) # Error [422] VALIDATION_FAILED: name is required ``` Note: `classify_error` redacts secrets from the exception **message** as well as from its details, so the output is always safe to display. ## Exception Hierarchy ``` CleverAgentsError ├── DomainError │ ├── ValidationError │ ├── BusinessRuleViolation │ ├── ResourceNotFoundError │ ├── ResourceConflictError │ └── PlanError ├── InfrastructureError │ ├── DatabaseError │ ├── NetworkError │ └── ExternalServiceError ├── ProviderError │ ├── RateLimitError │ ├── TokenLimitExceededError │ └── ModelNotAvailableError ├── AuthenticationError ├── AuthorizationError ├── ConfigurationError │ └── MissingConfigurationError ├── FileSystemError ├── ExecutionError └── StreamRoutingError ```