Docs

Error catalog

Stable JSON error codes, when to expect each, and how to recover programmatically.

Errors

Every Hangar error response is a JSON object with a stable code, a human message, and an optional structured detail.

Shape

{
  "error": "<machine-readable-code>",
  "message": "<human description>",
  "details": { "...": "optional" },
  "retryAfterSeconds": 0
}

The error code is machine-readable and stable across releases. The message is for human display. details carries Zod-style field errors on validation failures; retryAfterSeconds is set on 429 and mirrors the Retry-After header.

Code catalog

Code HTTP When to expect it What to do
unauthorized 401 Missing, malformed, or expired token / session. Re-authenticate; mint a new PAT if expired.
forbidden 403 Token is valid but lacks the required scope. Mint a token that includes the scope.
not-found 404 Resource doesn't exist for this caller. Check the id; resource may belong to another user.
validation 400 Request body or query failed Zod validation. Inspect details for field-level errors and fix the request.
invalid-json 400 Body could not be parsed as JSON. Send a valid JSON body with Content-Type: application/json.
rate-limited 429 Sliding-window limit exceeded. Honor retryAfterSeconds and back off.
wallet-empty 402 Wallet hit zero before the call ran. Top up via POST /api/wallet/topup and retry.
provider-error 502 Upstream LLM or billing provider failed. Retry with exponential backoff (4s, 8s, 16s, 32s).
missing-id 400 A required path or query parameter is missing. Add the parameter and retry.
conflict 409 Resource state conflicts with the requested change. Re-read state; the operation is not safe to retry blindly.
internal 500 Unhandled server error. Retry once; report on Discussions if it persists.

Examples

Validation

{
  "error": "validation",
  "message": "Request validation failed.",
  "details": {
    "name": ["String must contain at least 1 character(s)"]
  }
}

Wallet empty

{
  "error": "wallet-empty",
  "message": "Wallet balance is zero. Top up at /dashboard/wallet."
}

Rate limited

{
  "error": "rate-limited",
  "message": "Too many requests. Retry after 12s.",
  "retryAfterSeconds": 12
}

What never happens

  • We never return HTML on a 4xx/5xx response from /api/*. Always JSON.
  • We never include a stack trace or PII in production responses.
  • We never surface upstream provider error bodies verbatim — they are re-mapped to provider-error with a redacted details field.
Error catalog — Hangar