Skip to main content

D75 — DomainException optionally carries JSON:API source + meta (structured error-object members)

Architecture decision record, relocated verbatim from the retired single-file docs/tenancy/ decision log. Status, thematic clusters, and how to record a new ADR: the decision log index.

The client-facing error contract (errors[] rendered by JsonApiExceptionHandler) previously let a DomainException carry only a stable code + a human detail. It now ALSO carries two optional JSON:API error-object members — ?array $source (e.g. ['pointer' => '/data/attributes/…']) and ?array $meta (a free-form machine-readable map) — rendered onto the error object when present. Both default to null, so every existing throw site is unaffected; this is a pure superset of the prior contract (the D22 JSON:API envelope). The first consumer is role management: RoleComposer::composeOrFail() maps a RoleValidationException to a SINGLE ROLE_INVALID (422) error whose meta.invalid_permissions lists EVERY offending permission key (RoleValidationException now COLLECTS all unknown/cross-guard keys, each tagged unknown|cross_guard, rather than throwing on the first), plus meta.missing_dependencies where relevant, and — when exactly one key is at fault — a best-effort source.pointer=/data/attributes/permissions/{index} (ADJUDICATED: one error object with a meta map + best-effort source, NOT one-error-per-key). The Scramble #[DomainError] attribute is unchanged (status/code/detail only); the dynamic meta/source are runtime-only, not part of the static OpenAPI shape.


← Decision log index