Expected errors and HTTP mapping
View as MarkdownUse status codes to describe expected failures at the HTTP boundary:
404means a row is absent or invisible to the request.409means valid input conflicts with current resource state.422means the request parsed but the workflow cannot accept a value derived or validated by the server.502means an upstream system failed or returned unusable data.
Map the domain family once
Section titled “Map the domain family once”A domain module raises one typed expected-error family. In the task-completion slice, that family carries the declared status and strict feature payload. The adapter catches it once:
try { const auth = c.get("auth");
return { status: 200, body: completeTask({ db: c.get("db"), auth }, request.params.id), };} catch (error) { return taskCompletionErrorResponse(error);}taskCompletionErrorResponse() accepts only TaskCompletionError, covers the
family’s declared 404 and 409 variants, and rethrows everything else.
Unexpected database, programming, and infrastructure failures stay on the
central error path.
Declare each feature-owned status in the shared contract. Request parsing owns
the generic 400; shared authentication middleware owns 401 and 403 outside
the feature response map.
On the frontend, ApiError.body remains unknown. Parse the exported strict
feature schema before using a code for recovery, and require the declared
status/code pair. A schema-valid body with the wrong status is not a supported
recovery branch.