# 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
```
