Skip to content

ADR 0010 structured errors ambiguity is an error

Karthik P edited this page Oct 31, 2026 · 1 revision

ADR-0010: Structured errors; ambiguity is an error

Every failure is an error object with a stable code from the catalogue, a JSON path, a message and a hint where one helps, and an input with more than one reading returns an error listing every candidate instead of a guess.

Last reviewed: 31 Oct 2026

Status

Accepted on 31 Oct 2026. Decided by the research documents: design principle 9 of the research proposal and its section "Errors it can fix". Built in 0.1.0 for runtime ambiguity (AMBIGUOUS_LABEL). The validation-time check TEMPLATES_AMBIGUOUS is planned for 0.2 (Roadmap).

Context

People and agents both need to correct their own input. An agent can fix subdivision.periods if told the path and the expected sum; it cannot fix "invalid calendar". Labels are often ambiguous: "FY26" can mean different years under different schemes, and a two-digit year has no meaning without a century. A library that picks one reading gives a confident wrong answer.

Decision drivers

  • Errors must be machine-readable and identical across languages (ADR-0019).
  • No silent wrong answers.
  • Codes must be stable so that callers can branch on them.

Considered options

Option 1: Native exceptions with free-text messages

Pros:

  • Idiomatic in each language.

Cons:

  • Messages differ per language and change between releases; callers parse strings.

Option 2: Structured error objects with stable codes (chosen)

Pros:

  • Callers branch on code; tools jump to path.
  • The same error JSON in every language.

Cons:

  • Every new failure needs a catalogue entry, which is a public API change and needs an ADR under ADR-0001.

Option 3: Resolve ambiguity with a documented preference order

Pros:

  • Every input gets an answer.

Cons:

  • The preference is an organisation-specific choice made silently, which design rule 10 forbids.

Decision

Option 2, with ambiguity reported. AGENTS.md design rules 7 and 8:

  • Every error has code from the catalogue in spec/api-v1.md, a JSON path, a message, and a hint where one helps. Adding a code means adding it to the catalogue in the same pull request. No code path panics, throws a bare exception, or returns an error without a code.
  • When an input has more than one reading, the operation returns AMBIGUOUS_LABEL (or the relevant ambiguity code) with every candidate in details.

As built in 0.1.0:

  • The error object is {"code","path","message","hint","details"}. Validation reports every error it finds, sorted by path then code; other operations report one.
  • parse returns AMBIGUOUS_LABEL with details.candidates, ordered by start date, then scheme name, then level.
  • Two-digit years need an explicit century_pivot (CENTURY_PIVOT_MISSING otherwise); there is no implicit reference year.
  • The test suite feeds malformed input to every operation and checks that it never panics (crate README).

Consequences

Good:

  • Errors are data, so bindings can expose them natively (FiscalclockError in Python and JavaScript) while the JSON stays identical.
  • An ambiguous label is never misread.

Bad:

  • Without TEMPLATES_AMBIGUOUS in 0.1.0, a scheme whose templates can produce the same string for different periods is accepted, and the ambiguity appears only when a label is parsed.
  • The proposal says only the human message may differ across languages. In 0.1.0 the core produces message and hint, so they are identical too, but tests must not depend on their wording.

Links

Clone this wiki locally