Docs
Dashboard
View as Markdown
On this page
  1. Status codes
  2. In code

Errors

Every error is an RFC 9457 problem document with a stable type, a sentence for people and a path for every invalid field.

Any non-2xx answer has content-type: application/problem+json:

{
  "type": "https://shopfa.st/problems/validation",
  "title": "Validation failed",
  "status": 422,
  "detail": "The request body is invalid.",
  "fields": [
    { "path": "body.priceMinor", "message": "Too small: expected number to be >=0" }
  ]
}
  • type is stable: branch on its last segment (validation, forbidden, rate-limited, edit-conflict…). It is an identifier, not a page to fetch.
  • detail is one plain sentence you can show to a person.
  • fields lists every invalid input by dotted path: params.storeId, query.limit, body.items.0.quantity.

Status codes

Status When
400 The body is not JSON, or the request is malformed
401 No credentials, or a key or token that is not valid
403 Your scopes, role or IP allowlist do not cover this
404 It does not exist, or belongs to another store
409 A conflict: a limit, a state that does not allow this, an edit conflict
413 The body is larger than 6 MB
422 Validation failed (see fields)
429 Rate limited; wait for Retry-After seconds
5xx Our side. Safe to retry with backoff for GET, and for writes that carry an idempotency key

In code

const res = await fetch(url, init);
if (!res.ok) {
  const problem = await res.json();
  const kind = problem.type.split('/').at(-1);
  if (kind === 'validation') showFieldErrors(problem.fields);
  else if (res.status === 429) await sleep(Number(res.headers.get('retry-after') ?? 1) * 1000);
  else throw new Error(problem.detail);
}
Was this page helpful?