Metadata-Version: 2.4
Name: actionable-errors
Version: 0.1.0
Summary: Three-audience error framework: typed errors for code, actionable suggestions for humans, tool guidance for AI agents
Project-URL: Repository, https://github.com/grimlor/actionable-errors
Author: grimlor
License: MIT
License-File: LICENSE
Classifier: Development Status :: 3 - Alpha
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Typing :: Typed
Requires-Python: >=3.12
Provides-Extra: dev
Requires-Dist: mypy<2,>=1.13; extra == 'dev'
Requires-Dist: pre-commit<5,>=4; extra == 'dev'
Requires-Dist: pytest-asyncio<1,>=0.24; extra == 'dev'
Requires-Dist: pytest-cov<7,>=6; extra == 'dev'
Requires-Dist: pytest<9,>=8; extra == 'dev'
Requires-Dist: ruff<1,>=0.8; extra == 'dev'
Requires-Dist: taskipy<2,>=1.14; extra == 'dev'
Description-Content-Type: text/markdown

# actionable-errors

Three-audience error framework for Python.

Every `ActionableError` speaks to three audiences simultaneously:

| Audience | Uses | Provided by |
|----------|------|-------------|
| **Calling code** | Typed `ErrorType` for routing (retry? escalate? ignore?) | `ErrorType(StrEnum)` — 8 base categories |
| **Human operator** | `suggestion` + `Troubleshooting` steps | Frozen dataclasses with actionable text |
| **AI agent** | `AIGuidance` with concrete next tool calls | Frozen dataclass with tool suggestions |

## Install

```bash
pip install actionable-errors
```

**Zero runtime dependencies** — stdlib only. Sits at the bottom of every dependency tree.

## Quick Start

```python
from actionable_errors import (
    ActionableError,
    AIGuidance,
    ToolResult,
    from_exception,
    sanitize,
)

# Domain-specific factory with built-in defaults
error = ActionableError.authentication(
    service="Azure DevOps",
    raw_error="401 Unauthorized",
)
print(error.suggestion)    # "Check your credentials and try again."
print(error.ai_guidance)   # AIGuidance(action_required="Re-authenticate", command="az login")

# Auto-classify any exception
try:
    raise ConnectionError("Connection refused")
except Exception as exc:
    ae = from_exception(exc, service="Kusto", operation="query")
    print(ae.error_type)   # "connection"

# Typed result envelope for MCP tool responses
result = ToolResult.ok(data={"items": 42})
result = ToolResult.fail(error=error)     # extracts error_type + suggestion

# Credential sanitization
clean = sanitize('password="hunter2" token=abc123')
# → 'password="***" token=***'
```

## Extending ErrorType

Python `StrEnum` can't be subclassed once it has members, so extend via composition:

```python
from enum import StrEnum
from actionable_errors import ActionableError

class RAGErrorType(StrEnum):
    EMBEDDING = "embedding"
    INDEX = "index"

error = ActionableError(
    error="Vector store unavailable",
    error_type=RAGErrorType.INDEX,
    service="pinecone",
    suggestion="Check Pinecone cluster status.",
)
```

## Factories

Eight domain-specific factories with sensible defaults:

| Factory | Key Parameters |
|---------|---------------|
| `.authentication(service, raw_error)` | Default suggestion + AI guidance |
| `.configuration(field_name, reason)` | — |
| `.connection(service, url, raw_error)` | — |
| `.timeout(service, operation, timeout_seconds)` | — |
| `.permission(service, resource, raw_error)` | — |
| `.validation(service, field_name, reason)` | — |
| `.not_found(service, resource_type, resource_id, raw_error)` | — |
| `.internal(service, operation, raw_error)` | — |

All factories accept optional `suggestion`, `ai_guidance`, and `troubleshooting` kwargs.

## Development

```bash
# Install dev dependencies
uv sync --extra dev

# Run all checks
task check  # lint → type → test (90 tests, 100% coverage)
```

## License

MIT
