6. Error Handling#
Errors are where consistency matters most. If every service formats errors differently, debugging turns into chaos.
JsonDispatch enforces a clean, uniform structure for client-side issues (fail) and server-side issues (error) — so you always know where the problem came from.
6.1 fail vs error — Know the Difference#
Type |
Cause |
Who Fixes It |
Typical HTTP Status |
Example |
|---|---|---|---|---|
``fail`` |
The client sent something invalid |
The client |
400–429 (plus 406/410/415 for negotiation/versioning) |
Missing required fields, invalid format |
``error`` |
The server or an external dependency failed |
The server |
500–504 |
Timeout, DB crash, network outage |
Think of it like this:
🧑💻 fail → “Fix your request and try again.”
🧩 error → “It’s not you, it’s us.”
This split helps:
Clients decide whether to retry or correct their data.
Developers log and alert on real outages separately from validation noise.
6.2 Error Object Structure#
Both fail and error responses contain an array of objects under data. Each describes one issue in a standard format:
Field |
Type |
Description |
|---|---|---|
|
integer |
HTTP status code for this error. |
|
string |
Where the problem occurred — field path or subsystem name. |
|
string |
Short summary of the problem. |
|
string |
Human-readable explanation for logs or UI. |
Example – Client Validation (``fail``)
Response
HTTP/1.1 422 Unprocessable Entity
Content-Type: application/json; charset=utf-8
X-Api-Version-Selected: 1.3.1
X-Request-Id: 9d2e7f6a-2f3b-45b0-9ff2-dc3d99991234
{
"status": "fail",
"message": "Validation failed",
"data": [
{
"status": 422,
"source": "/data/attributes/email",
"title": "Invalid email",
"detail": "Email must be a valid address."
},
{
"status": 422,
"source": "/data/attributes/password",
"title": "Password too short",
"detail": "Password must be at least 8 characters."
}
]
}
Example – Server Outage (``error``)
Response
HTTP/1.1 503 Service Unavailable
Content-Type: application/json; charset=utf-8
X-Api-Version-Selected: 1.3.1
X-Request-Id: e13a97b3-5a2d-46db-a3b2-f401239b0cba
{
"status": "error",
"message": "Database unavailable",
"code": "DB_CONN_TIMEOUT",
"data": [
{
"status": 503,
"source": "db-service",
"title": "Timeout",
"detail": "No response from database after 30s."
}
]
}
6.3 Field-level vs request-level errors#
JsonDispatch lets you express where an issue occurred so clients can react precisely.
Two scopes
Field-level — a specific input is invalid. Use a JSON Pointer–style path in
source(e.g.,/data/attributes/email).Request-level — the whole request failed for a non-field reason (rate limit, auth, dependency outage). Use a concise string naming the subsystem or concern (e.g.,
auth,rate-limit,payments-gateway).
Rules
Prefer the most specific
sourceyou can provide.You may mix field-level and request-level items in the same
dataarray.Keep
titleshort; put human-friendly detail indetail.Don’t leak internals; subsystem names should be stable, public-safe identifiers.
Examples
Field-level (fail)#
{
"status": "fail",
"message": "Validation failed",
"data": [
{
"status": 422,
"source": "/data/attributes/email",
"title": "Invalid email",
"detail": "Email must be a valid address."
},
{
"status": 422,
"source": "/data/attributes/age",
"title": "Out of range",
"detail": "Age must be between 13 and 120."
}
]
}
Request-level (error)#
{
"status": "error",
"message": "Upstream dependency unavailable",
"code": "PAYMENTS_GATEWAY_DOWN",
"data": [
{
"status": 503,
"source": "payments-gateway",
"title": "Service unavailable",
"detail": "No response from provider within 30s."
}
]
}
Mixed (some fields invalid, plus a request constraint)#
{
"status": "fail",
"message": "Request cannot be processed",
"data": [
{
"status": 422,
"source": "/data/attributes/items/0/sku",
"title": "Unknown SKU",
"detail": "SKU ABC-123 was not found."
},
{
"status": 429,
"source": "rate-limit",
"title": "Too many requests",
"detail": "Burst limit exceeded; retry after 15 seconds."
}
]
}
Client guidance
Highlight fields with pointer paths; show inline messages near inputs.
Banner or dialog for request-level issues (
auth,rate-limit,maintenance).Consider
statuscode for retry/backoff behavior (see the next subsections).
6.4 Error Codes (code) — Symbolic and Actionable#
The optional top-level code field adds a business-level meaning beyond the HTTP status. It’s ideal for client logic, dashboards, and automation.
Guidelines
Use uppercase
UPPER_SNAKE_CASE.Keep them short and descriptive.
Document them in your internal or public API guide.
Stay consistent across microservices.
Examples
Code |
Meaning |
|---|---|
|
The requested user doesn’t exist. |
|
Database connection timeout. |
|
Payment provider not reachable. |
|
Validation failure on title. |
6.5 Recommended HTTP Status Mapping#
Scenario |
|
HTTP Status |
|---|---|---|
Read / List / Create / Update Success |
success |
200 / 201 / 204 |
Validation error / bad input |
fail |
400 / 422 |
Version header missing / malformed |
fail |
400 |
Unsupported |
fail |
406 |
Authentication required or failed |
fail |
401 |
Forbidden (no permission) |
fail |
403 |
Not found |
fail |
404 |
Conflict (duplicate / version mismatch) |
fail |
409 |
Requested version retired (past sunset) |
fail |
410 |
Unsupported request |
fail |
415 |
Server exception / crash |
error |
500 |
Bad gateway (upstream failure) |
error |
502 |
Service unavailable / maintenance |
error |
503 |
Upstream timeout |
error |
504 |
6.6 Key Takeaways#
Clients never send
X-Request-Id— the server generates it for traceability.All error and fail responses share the same envelope shape.
Each issue in
data[]must be explicit and readable.code+status+X-Request-Id= everything you need for quick debugging.