ADR 0003: Error Contract Governance

Ratification

Adopted before ADR 0018. There was no separate ratification process. Git history for this file on main is the record.

Context

Why this matters: Clients cannot rely on free-text messages; they change with wording and locale. Stable codes and keys let apps and partners branch on fixed values, log clearly, and show their own user-facing text. One-off errors also make incidents harder to fix: “something failed” in logs is not enough to act on.

As we add endpoints, we need one shared list of transport errors (auth, rate limits, idempotency) and domain-specific tables per entity, next to OpenAPI and tests.

Decision

We use one versioned error contract in code and OpenAPI:

  1. Error payloads are part of public API contract.
  2. Use stable numeric code and symbolic key.
  3. code + key are immutable once published.
  4. Contract evolves additively; existing semantics cannot be silently changed.
  5. Validation and business errors follow explicit, documented schemas.
  6. Error catalog is split into:
    • COMMON pool for service-wide reusable errors (security/rate-limit/idempotency/core transport).
    • Entity matrices for domain-specific errors (for example USER_*).

Contract model

Error catalog structure

Common reusable pool (service-wide)

Use COMMON_* for errors that should be reused consistently across all endpoints.

Entity matrices (domain-specific)

Use entity-prefixed codes for business/validation rules specific to one domain.

Implementation in this repository

Consequences

Positive

Trade-offs

Operating rules

  1. Add new codes; do not repurpose old ones.
  2. Keep fallback validation codes for unmapped errors.
  3. Prefer shared COMMON response blocks for cross-cutting errors.
  4. Entity routers must keep their own matrix for domain-only errors.
  5. Update OpenAPI examples and docs in the same change.
  6. Ensure make docs-fix and make release-check pass before release.

Page history

Date Change Author
Added Page history section (repository baseline). Ivan Boyarkin