Sign in
Docs
0%

Concepts

Errors

One envelope on every surface, a fixed set of codes, and what to do with each.

The envelope

{
  "code": "bad_request",
  "error": "subject_id is required",
  "details": [{ "type": "field", "field": "subject_id", "description": "is required" }],
  "request_id": "0199a1f2-7c3e-7d41-9b2a-5e8f0c6d1a23"
}

code is a stable slug from the table below and the HTTP status follows it; error is a sentence for a person; details are typed entries; request_id is the call's x-request-id, the header every answer carries. The field names and the slugs are frozen: a client is written against them.

codeHTTPWhat to do
bad_request400fix the request; a field detail names the field
failed_precondition400the resource is in a state that refuses this call
unauthenticated401no token, or an expired one: fetch a new token
forbidden403the token is valid and this route is not yours
not_found404no such route or resource
already_exists409the thing you created exists
conflict409the call raced another; read and retry
payload_too_large413the body is over 2 MiB
unsupported_media_type415the body is not application/json
unprocessable422an Idempotency-Key you sent before, with another body: use a new key for a new write
rate_limited429wait Retry-After seconds; a retry detail carries it too
quota_exceeded429the account's units for the month are used up; see Usage and units
cancelled499you cancelled
internal500our fault; the sentence is internal error and the cause is in our logs under the request's trace
unimplemented501the route exists and does nothing yet
unavailable503a backend is down; retry with backoff
timeout504a backend was too slow; retry with backoff

Details

  • {"type":"field","field","description"}: which field and why.
  • {"type":"info","reason","domain","metadata"}: a machine-readable reason.
  • {"type":"retry","after_seconds"}: when to try again; Retry-After carries the same.

Per transport

REST sends the envelope as the body with the HTTP status. Server-sent events send it as the data of an event: error and end the stream. The WebSocket sends {"type":"error","id","code","error","details"} for a call, or without id when the frame itself was refused. MQTT publishes the envelope to the call's response topic; a refused packet gets an MQTT reason code (MQTT).

Which of these to retry, and how to retry a write without running it twice, is on Paging, retries and errors.