forked from HAL9000/cleveragents-core
f66fb5a19a
Align Unicode box-drawing blocks and related tables in specification.md, ADR-044/045/046, and reference pages for consistent MkDocs rendering. ISSUES CLOSED: #1171
197 lines
6.5 KiB
Markdown
197 lines
6.5 KiB
Markdown
# 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
|
|
```
|