ISSUES CLOSED: #320
6.5 KiB
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.
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 |
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:
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
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:
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
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