Observed since 20 Oct, 18:32 UTC
No superseded metadata states collected. Unchanged observations do not create new revisions.
actionable-errors
Three-audience error framework: typed errors for code, actionable suggestions for humans, tool guidance for AI agents
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 |
pip install actionable-errors
Zero runtime dependencies — stdlib only. Sits at the bottom of every dependency tree.
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=***'
Python StrEnum can't be subclassed once it has members, so extend via composition:
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.",
)
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.
# Install dev dependencies
uv sync --extra dev
# Run all checks
task check # lint → type → test (90 tests, 100% coverage)
MIT
Download counts and popularity will appear here as the collector receives them.
| Package / target | Requirement | Relationship |
|---|---|---|
pre-commitEnvironment{
"extras": "",
"marker": "extra == \"dev\"",
"requires_python": ">=3.11",
"evaluation": "NOT_EVALUATED"
} | pre-commit<5,>=4; extra == "dev" | runtime |
pyrightEnvironment{
"extras": "",
"marker": "extra == \"dev\"",
"requires_python": ">=3.11",
"evaluation": "NOT_EVALUATED"
} | pyright<2,>=1.1; extra == "dev" | runtime |
pytest-asyncioEnvironment{
"extras": "",
"marker": "extra == \"dev\"",
"requires_python": ">=3.11",
"evaluation": "NOT_EVALUATED"
} | pytest-asyncio<1,>=0.24; extra == "dev" | runtime |
pytest-covEnvironment{
"extras": "",
"marker": "extra == \"dev\"",
"requires_python": ">=3.11",
"evaluation": "NOT_EVALUATED"
} | pytest-cov<7,>=6; extra == "dev" | runtime |
pytestEnvironment{
"extras": "",
"marker": "extra == \"dev\"",
"requires_python": ">=3.11",
"evaluation": "NOT_EVALUATED"
} | pytest<9,>=8; extra == "dev" | runtime |
ruffEnvironment{
"extras": "",
"marker": "extra == \"dev\"",
"requires_python": ">=3.11",
"evaluation": "NOT_EVALUATED"
} | ruff<1,>=0.8; extra == "dev" | runtime |
taskipyEnvironment{
"extras": "",
"marker": "extra == \"dev\"",
"requires_python": ">=3.11",
"evaluation": "NOT_EVALUATED"
} | taskipy<2,>=1.14; extra == "dev" | runtime |
bdist_wheel · 10.29 KB · Python py3
sdist · 82.43 KB · Python source
Observed since 20 Oct, 18:32 UTC
No superseded metadata states collected. Unchanged observations do not create new revisions.
package_version:7fa24be34ffd38013bfd7b0477d416a33debd77dbf2ac1388945fc282ba6c377