---ISSUE---
Problem
Three error extractors read a message field that the backend (FastAPI) does not produce. bulkImportService.extractErrorMessage:
return (
apiError.response?.data?.message ||
apiError.message ||
"Something went wrong during bulk import."
);
and sla.ts's extractErrorMessage:
return (
axiosError.response?.data?.message ||
axiosError.message ||
"An unexpected error occurred."
);
FastAPI error bodies use detail (a string or a [{loc, msg, type}] array), not message. Consequences:
- Every API validation error surfaces as the generic fallback: a bulk import rejected for a bad row shows "Something went wrong during bulk import." — the actual server reason (row 14: invalid severity) is discarded; SLA calculate/preview failures show "An unexpected error occurred." regardless of the server's message.
- The two services disagree with the rest of the app:
normalizeApiError (src/lib/errors.ts) and handleApiError in src/services/outages.ts do read detail — so error UX varies by service: outages shows real reasons, bulk import and SLA hide them.
- The mismatch is invisible to typechecking:
response?.data is untyped in these helpers, so the wrong field name compiles cleanly.
Root cause
The extractors were written against a message-first API shape before FastAPI's detail convention was established, and were never aligned.
Why this is architecturally hard
- The correct fix is one shared error-extraction helper (reading
detail, handling the string-vs-array forms, falling back to message) reused by all services — a consolidation (companion issue) that must preserve each service's fallback text.
- The array form of
detail (422 validation) needs different rendering than the string form (a generic 400) — the extractor must decide what a merged message looks like, aligning with the normalizeApiError field-level work.
- Tests must pin the mapping: mock FastAPI-shaped responses (
{detail: "..."} and {detail: [{loc, msg}]}) for each service and assert the surfaced message.
Proposed design
Add a shared extractApiErrorMessage (reads detail string/array, then message, then a fallback), use it in bulkImportService, sla.ts, and (optionally) outages.ts's handleApiError, and add table-driven tests per service.
Acceptance criteria
Service
Tests
Out of scope
Field-level 422 rendering (tracked separately) and backend error-shape changes.
Getting started
Good first files to read: src/services/bulkImportService.ts, src/services/sla.ts, src/services/outages.ts.
---ISSUE---
Problem
Three error extractors read a
messagefield that the backend (FastAPI) does not produce.bulkImportService.extractErrorMessage:and
sla.ts'sextractErrorMessage:FastAPI error bodies use
detail(a string or a[{loc, msg, type}]array), notmessage. Consequences:normalizeApiError(src/lib/errors.ts) andhandleApiErrorinsrc/services/outages.tsdo readdetail— so error UX varies by service: outages shows real reasons, bulk import and SLA hide them.response?.datais untyped in these helpers, so the wrong field name compiles cleanly.Root cause
The extractors were written against a message-first API shape before FastAPI's
detailconvention was established, and were never aligned.Why this is architecturally hard
detail, handling the string-vs-array forms, falling back tomessage) reused by all services — a consolidation (companion issue) that must preserve each service's fallback text.detail(422 validation) needs different rendering than the string form (a generic 400) — the extractor must decide what a merged message looks like, aligning with thenormalizeApiErrorfield-level work.{detail: "..."}and{detail: [{loc, msg}]}) for each service and assert the surfaced message.Proposed design
Add a shared
extractApiErrorMessage(readsdetailstring/array, thenmessage, then a fallback), use it inbulkImportService,sla.ts, and (optionally)outages.ts'shandleApiError, and add table-driven tests per service.Acceptance criteria
Service
detailstring and array forms map to readable messages.Tests
detailstring,detailarray,message, and absent-body cases.Out of scope
Field-level 422 rendering (tracked separately) and backend error-shape changes.
Getting started
npm testGood first files to read:
src/services/bulkImportService.ts,src/services/sla.ts,src/services/outages.ts.