9.2 Structured errors (application/problem+json)
Every failure in RS2 — from any service, prebuilt or custom — is returned as
application/problem+json with a machine-readable code. There are no bare
string errors. This uniformity means a client can handle failures
programmatically across the whole surface.
The shape
{ "type": "https://rs2.dev/errors#validation_failed",
"title": "Unprocessable Entity",
"status": 422,
"code": "validation_failed",
"detail": "…",
"tenant": "acme",
"traceId": "…",
"retryable": false,
"errors": [ { "path": "/total", "error": "…" } ] }
code— the stable, machine-readable identifier; branch on this, not ontitleorstatustext.retryable— whether retrying might succeed.traceId— correlates to the log line for this request (9.6).errors— present for validation failures: an array of{path, error}.
The codes
| code | status | notes |
|---|---|---|
bad_request |
400 | |
path_unsafe |
400 | rejected by the router for every service (3.2) |
unauthorized |
401 | missing/invalid credentials; lockout adds Retry-After |
forbidden |
403 | principal lacks a required role |
capability_denied |
403 | sandboxed code used an ungranted capability; extra capability field |
not_found |
404 | |
conflict |
409 | config version mismatch (If-Match), in-flight idempotent duplicate |
payload_too_large |
413 | |
validation_failed |
422 | extra errors array |
idempotency_key_reuse |
422 | same key, different payload |
internal |
500 | |
engine_unavailable |
501 | build lacks the engine for this code type |
contract_violation |
502 | custom service broke the contract (bad output, crash, compile failure) |
limit_exceeded |
503 | retryable; extras limit, observed, cap, retryAfterMs |
Pipeline failures carry step detail
A failing pipeline merges a pipeline object into the body (7.9):
"pipeline": { "failedStep": "/2",
"steps": [ { "step": "/0", "kind": "call", "status": 200 }, … ] }
So you can see which step failed and how far execution got, without re-running.
Handling errors well
- Switch on
code, not on status text — codes are stable. - Respect
retryableandRetry-After/retryAfterMs— don't hammer alimit_exceeded. - Log the
traceIdwhen reporting a problem; it's the key to the server-side log line (9.6). - For
validation_failed, surface theerrors[].pathto the user — they're JSON Pointers into the offending document.
Next: idempotency, which lets you safely retry the requests that are retryable.
← Previous: 9.1 The discovery surface · Manual home · Next: 9.3 Idempotency keys and replay →