Skip to main content

Errors

Every uniqOS error response uses one envelope, so you can handle them uniformly across endpoints. The SDKs map each one to a typed error class.

The error envelope

A non-2xx response carries a single error object:

{
"error": {
"code": "rate_limit_exceeded",
"message": "Rate limit exceeded. Retry after 12 seconds.",
"type": "rate_limit",
"request_id": "req_...",
"details": null,
"documentation_url": "https://docs.uniqos.ai/errors"
}
}
  • code — the machine-readable error code. This is authoritative: two errors can share an HTTP status but differ by code (see rate-limit vs quota below).
  • message — a human-readable description.
  • type — the error family.
  • request_id — include this when contacting support; it traces the exact call.
  • details — optional structured context (e.g. the offending fields on a validation error).
  • documentation_url — a pointer to the relevant docs.

The request_id is on every response, success or error. In the SDKs it is err.requestId (TypeScript) / err.request_id (Python) on a thrown error, and available via .withResponse() on a successful call.

Status codes

HTTPcodeMeaning
400 / 422validation_errorThe request payload was invalid; details lists the offending fields.
401authentication_errorMissing, malformed, or expired credentials.
402payment_requiredBilling issue — e.g. an unpaid invoice or suspended subscription.
403permission_deniedAuthenticated, but not allowed to perform the operation.
404not_foundThe addressed resource does not exist.
409conflictConflict with current state (e.g. a duplicate).
429rate_limit_exceededToo many requests. Retryable — honor Retry-After.
429quota_exhaustedPlan quota used up. Not retryable — upgrade or wait.
4xxguardrail_blockedA personality/engine guardrail blocked the request.
500internal_errorAn unexpected server error.
502upstream_llm_errorAn upstream LLM provider failed.
503service_unavailableTemporarily unavailable.
504turn_timeoutThe turn exceeded the engine budget and was aborted. Not billed.

Two 429s, told apart by code

A 429 can mean two very different things, distinguished by code:

  • rate_limit_exceeded — you are going too fast. It is retryable; the response carries a Retry-After header, which the SDKs surface as retryAfterSeconds (TypeScript) / retry_after_seconds (Python) and honor automatically on retry.
  • quota_exhausted — your plan's allotment is used up. The SDKs never retry it; upgrade your tier or wait for the next cycle.

504 turn_timeout is not auto-retried

When a turn exceeds the server's engine budget, uniqOS aborts it and returns 504 turn_timeout. That aborted turn is not billed — no turn is consumed and no active relationship is counted (this is the server-side guarantee in ADR-0009).

The SDKs do not auto-retry a 504, on purpose. A budget timeout means the turn was already too slow; a blind retry risks another long turn — and, if a client gives up while the server is still finishing, it can leave the original turn billable and start a second one. To keep that from happening, the SDKs also default their request timeout above the server's turn ceiling (~75s), so the client receives the server's clean, unbilled 504 rather than abandoning the turn early. Retrying a 504 is therefore your explicit choice, never the default.

The SDKs map this to a TurnTimeoutError. See the SDK error handling sections.

What the SDKs retry

Both SDKs retry network errors, 429 rate_limit_exceeded, and 500 / 502 / 503 with exponential backoff and jitter, honoring Retry-After. They never retry 429 quota_exhausted, 401, 403, other 4xx, or 504 turn_timeout.