ADR-0036: Error handling
Note
Architecture decision: accepted.
- Date: 2026-09-14
- Deciders: Founding maintainer
- Supersedes: none
- Superseded by: none
- Related: ADR index, Error handling, ADR-0013, ADR-0034, ADR-0002
Context and problem statement
Shiin’s code handles untrusted input from agents and hosts, and its error handling must never let a failure let an action through. At the same time, operators need clear diagnostics to fix configuration errors, and internal bugs must be caught during testing. The project needed a taxonomy of failure that separates authorization decisions from operational errors and diagnostics.
Decision drivers
- Decisions (
allow,deny,pending) are not errors and never becomeErr. - Operational errors must produce fail-closed
deny. - Diagnostics must be actionable for operators.
- Bugs must be caught in tests, not silently handled.
Considered options
- A four-category taxonomy: decisions, operational errors, user diagnostics, and bugs.
- A single
Result<Decision, Error>with errors propagated everywhere. - Panic on any unexpected condition and let the host handle it.
Decision outcome
Chosen option: “Four-category taxonomy”, because it keeps concerns separated and ensures failures always produce deny.
- Decisions (
Ok(Decision)):allow,deny,pending. NeverErr. - Operational errors (per-crate
thiserrorenums,#[non_exhaustive]): unreadable files, IPC timeouts, malformed payloads. Mapped to fail-closeddenyby one conversion function. - User diagnostics (with source spans, rendered by
miette): syntax errors in policy files. Shown to the operator, never to the agent. - Bugs (
debug_assert!, invariant violations): fail during testing; production failures producedenyvia the panic hook.
Consequences
- Good, because the single conversion function means there is one place to review and test.
- Good, because diagnostics are separated from operational errors.
- Bad, because the taxonomy adds upfront design cost.
Confirmation
- Every operational error routes through the single
deny_on_failurefunction. - A fault-injection test makes the hook panic and asserts the result is
deny.
Pros and cons of the options
Four-category taxonomy
- Good, because it separates concerns and ensures fail-closed.
- Bad, because it is more structured than a single error type.
Single Result with errors everywhere
- Good, because it is simple.
- Bad, because it conflates decisions with failures and risks letting
?turn adenyinto an error path.
Panic on unexpected conditions
- Good, because it crashes fast.
- Bad, because hosts may treat a crash as permission to proceed.
More information
The error handling engineering page describes the implementation. Each error variant has a stable code listed in error codes.