Cross-cutting · Error presentation & API error mapping
The shared plumbing that turns model validation failures and API error payloads into field-level messages and page banners, plus the custom error classes used to signal illegal states and disallowed operations.
| Area | Cross-cutting |
| Routes | (every form) |
| Centre modules | always available |
| Staff levels | all |
| Models | — |
| UI label | user-error / api-error |
| Coverage | 4 use cases · 2 happy · 5 failure |
Known issues (1)
How it works
Three layers. (1) parseErrors converts an ember-model-validator errors array into a field-keyed object the form components render inline. (2) mapErrors(e, intl.t) translates an API error object, and the better controllers (campaign, proposal, children, play centre) feed it into an errorDetails list. (3) Classification: '4' === adapterError.errors[0].status.charAt() chooses user-error (‘Something is wrong. Please check the following errors:’) for client errors versus api-error (“Something is wrong and it’s not your fault. Please, contact support…”) for server errors — a genuinely good distinction, though it throws if the payload has no string status. The generic fallback is info.default.error.message/info.default.error.title. Custom error classes (NoCookieError, IllegalStateError, NotAllowedError, IllegalArgumentError) are thrown for programming and permission violations; these surface as exceptions, not UI messages. A recurring weakness across the codebase is the bare .catch(() => {}), which hides the reason for a failure — notably on segment refresh, sales-year rejection communications and sales-year enable/disable.
Use cases
Why someone opens this (4)
- Showing a user exactly which fields are wrong without a page reload.
- Distinguishing ‘you made a mistake’ from ‘our server failed’ so support tickets land in the right place.
- Letting the API drive a translated message via
errors[0].meta['detail-key'].- Guarding developer mistakes with explicit exceptions rather than silent misbehaviour.
Path map
flowchart LR F(["Error presentation and API error mapping"]):::feat F --> H1["Validation errors shown inline"]:::ok F --> H2["API error classified"]:::ok F -.-> X1["Error payload without a status"]:::bad F -.-> X2["Swallowed failure"]:::bad F -.-> X3["Stuck loading flag"]:::bad F -.-> X4["Untranslated message"]:::bad F -.-> X5["Developer-facing exception"]:::bad classDef feat fill:#8c5a3c,stroke:#8c5a3c,color:#ffffff,font-weight:bold classDef ok fill:#e3f1ea,stroke:#0d6b42,color:#0d4a2e classDef bad fill:#f7e6e2,stroke:#9a2d1c,color:#6e1f13
Happy paths
Validation errors shown inline
- User submits an incomplete form.
model.validate()fails andparseErrorskeys the messages by field.nf-errorrenders each message beside its input and the page scrolls to the top.
API error classified
- Save is rejected with a 422.
mapErrorstranslates each error intoerrorDetails.- Because the status starts with 4, the
user-errorbanner is shown with the detail list.
Failure paths
Error payload without a status
Cause — API returns errors lacking a string
status.What happens —
status.charAt()throws inside the error handler, so the original failure is replaced by a crash.
Swallowed failure
Cause — A bare
.catch(() => {})in the calling code.What happens — Nothing is shown; the operation appears to have done nothing. Present in segment refresh, sales-year rejection, sales-year enable/disable and several others.
Stuck loading flag
Cause — A promise chain with a
.then()that sets a flag but no.catch()to clear it.What happens — The spinner never clears — the pattern behind the export, segment-save and AI-filter failure modes.
Untranslated message
Cause — A message built in code rather than from the locale file, e.g. the extras’
"<label> is required".What happens — Shown as-is in English regardless of the user’s language.
Developer-facing exception
Cause — A
TypeError/NotAllowedError/IllegalStateErrorguard fires.What happens — Surfaces as an unhandled exception rather than a message — correct for genuine programming errors, less so for the permission cases in intranet communications.
QA checklist
Tick these off while regression-testing this feature. The boxes are plain Markdown, so progress is visible in git diffs.
- ✅ Validation errors shown inline
- ✅ API error classified
- ⛔ Error payload without a status
- ⛔ Swallowed failure
- ⛔ Stuck loading flag
- ⛔ Untranslated message
- ⛔ Developer-facing exception
Implementation
app/utils/parse-errors-array-to-errors-object.jsapp/utils/map-errors.jsapp/components/nf-error.jsapp/components/nf-info.jsapp/routes/form.jsapp/errors/app/services/show-confirm-deletion.js
Translation keys (7)
Key English user-errorSomething is wrong. Please check the following errors: api-errorSomething is wrong and it’s not your fault. Please, contact support with the following information: info.default.error.messageAn error occurred. Please try again later info.default.error.titleError error.serverServer error required{attr} is required input.default.max-char.error.messageYou have reached maximum input character!
Related
- Same area — CSV import engine · Data export engine (email delivery) · Filter & segment builder component · File upload, gallery & image modal · Multi-language content authoring · Realtime updates (Pusher) · Role & module permission layer · Product analytics & error monitoring
Generated from doc/feature-inventory.json · id xc-error-handling · commit 276788ca6