ADR-0009: Errors: thiserror in libs, anyhow at the edge, no panics
- Status: Accepted
- Date: 2026-06-10
Context
Margin consumes untrusted input (arbitrary patches on stdin, arbitrary repo states) inside a raw-mode terminal. A panic in raw mode doesn’t just crash — it leaves the user’s terminal broken. And from v0.2, Margin holds write capabilities over the user’s working tree, where “crashed mid-operation” must have a defined answer.
Decision
- Library crates (
margin-core,margin-vcs,margin-tui) never panic on input. They return typed errors (thiserror-derived enums). Workspace lints warn onunwrap/expect; CI runs clippy with-D warnings, so every use needs an explicit#[allow]with a justification comment. Slice indexing on untrusted data usesget(). - The binary uses
anyhowfor context-rich error chains at the edge:margin: cannot open repository at ./x: not a git repository— message first, chain below, exit code 2 or 1 per ADR-0007. - A panic hook + terminal guard restore the terminal (leave raw mode, alternate screen) before any abort, then print the panic with an issue-link footer. The guard is RAII so early returns restore too.
- Recoverable runtime errors are
Msgs, rendered as a status-line message in the UI (e.g., reload failed because the repo is mid-rebase), never a crash, never a silent swallow. EveryErris either shown to the user or converted to a typed state — discarding errors is a review reject. - Fuzzing enforces rule 1 for the parser (ADR-0010).
Consequences
- “Never breaks your terminal, never eats an error” becomes testable, not aspirational.
- Error messages are a designed surface: libs carry structured causes, the edge renders them with context.
- Cost: typed error enums are more work than
anyhoweverywhere — accepted, because callers (the TUI) must distinguish failures to react correctly.
Alternatives considered
anyhoweverywhere — fine at the edge, but erases the type information the TUI needs to decide between “show message” and “degrade view”.- Panic-as-control-flow with
catch_unwind— hides logic errors and is unacceptable in the same process that applies patches to the index.