D10 · Errors from Postgres use stable keys
Accepted · 2026-10-02 · zuki
Decision: every refusal a database function or trigger raises carries a stable key and its parameters: raise exception using message = 'gate.debt_unapproved', detail = a JSON object with the values the message needs. Keys are named domain.reason, in lower case. apps/web turns keys into Indonesian or English text through one mapper; an unknown key shows a generic message and is reported to Sentry.
Why: the database owns the rules (D3), so it owns the words for breaking them. Without keys, every page parses exception text, and errors can't be translated (D7).
Consequences: each new key gets a pgTAP test that asserts it, a line in the module's Interface section (Backend Engineer), and text in both languages (Frontend Engineer). Where the text lives follows OQ17.