ADR-0010 — Error codes are public, semver-covered API¶
Status: Accepted Date: 2026-09-19 Related: ADR-0003, ADR-0008 Related: ADR-0017 — timeout naming and the no-builtin-shadowing rule
Context¶
Two consumers read errors here, and both benefit from the same thing. Humans want the fix, not a stack trace: the models are Rust's error codes, Pydantic v2's per-error URLs, Ruff's rule pages. LLM coding assistants want a stable, greppable token they can map to a known remedy — an error string that changes wording between releases is useless to them.
Adapters also sit on top of provider SDKs whose exceptions are unstable and provider-specific. Leaking those through is the same lock-in the library exists to remove.
Decision¶
- Every exception derives from
SandboxErrorand carries.code(SBX_E1002), a one-line cause,.hintwith the exact fix command, and.urlto a per-code docs page. - Codes are semver-covered API. Renaming or repurposing a code is a breaking change. One docs page per code, at a redirect-stable path.
- Adapters map native exceptions into the sandboxio tree, preserving
__cause__. A raw provider exception escaping an adapter is a bug, and the contract suite checks for it. - Hints are specific, not generic: auth errors name the exact missing environment variable;
unknown-backend errors list what is installed and how to install what is not;
CapabilityNotSupportednames which backends do support the feature. - Error-catalog docs pages are generated from the catalog in code, so codes, hints and docs cannot drift.
- No exception inherits from a builtin, and no name shadows one (ADR-0017).
Consequences¶
- The catalog is a design artifact maintained alongside the code, not a docs afterthought. Adding an error means adding a code, a hint and a page.
- Stable codes constrain refactoring: merging two error conditions is breaking even when the class hierarchy would allow it.
- Good hints require knowing the user's situation — which backends are installed, which env var is missing. That argues for errors constructed with context rather than raised bare, and is worth the plumbing.