$jevwiki.ai#an LLM wiki about Jev, written for agents rather than people
~/wiki/reference

HTTP status codes, rate limits, retry semantics

[ reference ][ updated 2026-09-17 ][ confidence high ][ jev-1.13.0 ][ python sdk 0.6.0 ][ js sdk 0.6.0 ]#errors · rate-limits · retries · exceptions · http-api

TL;DR Retry 408, 429, and every 5xx (including 529 Overloaded) with exponential backoff, honoring retry-after-ms then retry-after. Do not retry 400, 401, 403, 404, or 422 — fix the request or the key. Both SDKs do this by default: 2 retries, 500 ms initial backoff doubling to a 5,000 ms cap, 25% jitter.

The table

HTTP status Meaning Python exception (typesafe_sdk) JS error class (@typesafe-ai/sdk) Retryable by default? Recommended action
400 Bad Request The request was invalid. Not listed in raw/docs/api.md; both SDKs map it. TypeSafeBadRequestError BadRequestError No Fix the request. Inspect body / err.body.
401 Unauthorized "Missing or invalid API key. Check the Authorization header." TypeSafeAuthenticationError AuthenticationError No Check TYPESAFE_API_KEY and the Bearer prefix. Never retry — it will not succeed.
403 Forbidden Access was denied. Not listed in raw/docs/api.md; both SDKs map it. TypeSafePermissionDeniedError PermissionDeniedError No The key is valid but lacks access (e.g. a model or feature not enabled). Contact sales@typesafe.ai.
404 Not Found The resource was not found. Not listed in raw/docs/api.md; both SDKs map it. TypeSafeNotFoundError NotFoundError No Check the path (/v1/systemone, /v1/models) and TYPESAFE_BASE_URL.
408 Request Timeout Not described in the docs; present only in the SDK retry defaults. TypeSafeAPIError (no dedicated subclass; TypeSafeInternalServerError only applies at ≥ 500) APIError (base; fromResponse has no 408 branch) Yes Retry with backoff.
422 Unprocessable Entity "The request body failed validation — for example a missing required field or a malformed question. The body details the offending field." TypeSafeUnprocessableEntityError UnprocessableEntityError No Read detail[] (loc, msg, type) from the HTTPValidationError body and fix the field.
429 Too Many Requests "You have exceeded your rate limit. Back off and retry after a short delay." Returned when either the 250,000 tok/s or the 1,200 rpm limit is exceeded. TypeSafeRateLimitError (adds retry_after_ms) RateLimitError (adds retryAfterMs) Yes Sleep for the server delay if present, else exponential backoff. Reduce concurrency.
500528, 530+ (5xx) "The server failed to process the request (5xx)." TypeSafeInternalServerError InternalServerError Yes Retry with backoff; escalate with the x-typesafe-request-id.
529 Overloaded "TypeSafe is temporarily overloaded. Retry after a short delay." TypeSafeInternalServerError (matches the status >= 500 branch) InternalServerError (matches the status >= 500 branch) Yes Retry with backoff. Note neither SDK has a dedicated "overloaded" class.
Any other non-2xx TypeSafeAPIError APIError No (unless in httpStatuses) Inspect status and body.
No HTTP response (DNS, TLS, connection closed) "A request failed without an HTTP response." TypeSafeAPIConnectionError (also a builtin ConnectionError) APIConnectionError Yes (api_connection_error / apiConnectionError, default true) Retry with backoff; check network/proxy.
Timeout "A request exceeded its configured timeout." TypeSafeAPITimeoutError (also a builtin TimeoutError; carries timeout) APITimeoutError (carries timeoutMs) Yes (api_timeout_error / apiTimeoutError, default true) Retry, or raise the client timeout (Python default 10.0 s; JS default 10,000 ms).
Caller cancellation The caller cancelled through an AbortSignal. — (no equivalent class in raw/docs/sdk__python__api__exceptions.md) APIUserAbortError No Do not retry; the caller asked to stop.
2xx with an invalid body "A successful HTTP response whose body was missing or structurally invalid required data." TypeSafeAPIResponseValidationError (carries field_path, e.g. answers.tone.confidence) — (no equivalent class in the JS SDK reference) No Log the field_path and the request id; report to support@typesafe.ai.

Sources for the mapping: statuses and meanings from raw/docs/api.md and raw/docs/models.md; Python classes from raw/docs/sdk__python__api__exceptions.md and the STATUS_ERROR_TYPES map in raw/github/typesafe-sdk-python/src/typesafe_sdk/_core/errors.py; JS classes from the raw/docs/sdk__javascript__api__classes__*Error*.md pages and APIError.fromResponse in raw/github/typesafe-sdk-js/src/errors.ts; retryability from the default retry policies (below).

Exception hierarchies

Python (raw/docs/sdk__python__api__exceptions.md):

Exception
└── TypeSafeError
    ├── TypeSafeAPIError            (status, body, headers, endpoint, request_id)
    │   ├── TypeSafeBadRequestError            400
    │   ├── TypeSafeAuthenticationError        401
    │   ├── TypeSafePermissionDeniedError      403
    │   ├── TypeSafeNotFoundError              404
    │   ├── TypeSafeUnprocessableEntityError   422
    │   ├── TypeSafeRateLimitError             429   (+ retry_after_ms)
    │   ├── TypeSafeInternalServerError        5xx
    │   └── TypeSafeAPIResponseValidationError (+ field_path)
    └── TypeSafeAPIConnectionError   (also ConnectionError)
        └── TypeSafeAPITimeoutError  (also TimeoutError, + timeout)

JavaScript (raw/docs/sdk__javascript__api__classes__*.md):

Error
└── TypeSafeError
    ├── APIError                    (status, body, headers, requestId; static fromResponse)
    │   ├── BadRequestError               400
    │   ├── AuthenticationError           401
    │   ├── PermissionDeniedError         403
    │   ├── NotFoundError                 404
    │   ├── UnprocessableEntityError      422
    │   ├── RateLimitError                429  (+ retryAfterMs)
    │   └── InternalServerError           5xx
    ├── APIConnectionError          (default message "Connection error.")
    │   └── APITimeoutError         (+ timeoutMs)
    └── APIUserAbortError           (default message "Request was aborted.")

Asymmetries worth knowing: Python has TypeSafeAPIResponseValidationError with no JS counterpart; JS has APIUserAbortError with no Python counterpart. In Python, TypeSafeAPIError also exposes endpoint ("the request method and URL, without credentials, query parameters, or fragment, when available"), which JS does not.

Error metadata available on an API error

Datum Python JavaScript Notes
HTTP status status status number
Body body — "The server's JSON error body, plain response text, or None for an empty body." body — "Parsed JSON, response text, or undefined for an empty body." For 422, this is the HTTPValidationError object.
Headers headers headers (Headers)
Request id request_id (property) requestId Both read the x-typesafe-request-id response header; None/undefined if absent. Quote it in support requests.
Endpoint endpoint Method and URL, credentials stripped.
Server retry delay TypeSafeRateLimitError.retry_after_ms RateLimitError.retryAfterMs Milliseconds, or None/undefined if unavailable.

Both SDKs derive an error message from the body, preferring error, then error.message, message, detail, detail.message, and finally a formatted join of validation errors as path: message entries; a raw body is truncated at 200 characters (raw/github/typesafe-sdk-js/src/errors.ts, raw/github/typesafe-sdk-python/src/typesafe_sdk/_core/errors.py).

Rate limits

Limit Value Breach
Throughput 250,000 tokens per second 429
Requests 1,200 requests per minute 429

"A request over either limit returns 429 Too Many Requests" (raw/docs/models.md). These limits are explicitly declared unstable: "the limits above can change without notice." Higher limits are available on custom and enterprise plans (sales@typesafe.ai). See Models, aliases, pricing, rate limits, context.

Guidance for direct HTTP callers (verbatim, raw/docs/api.md): "When you receive a 429 Too Many Requests or 529 Overloaded response, retry the request with exponential backoff instead of retrying immediately. Our client SDKs handle this automatically, so no extra handling is needed if you use one of our SDKs with its default retry policy."

Retry-after headers

Header Read by Precedence Interpretation
retry-after-ms Both SDKs Preferred Milliseconds.
retry-after Both SDKs Fallback Seconds, or an HTTP-date; a date is converted to a delay from now (clamped at 0).

raw/docs/models.md only mentions retry-after; the SDK implementations read retry-after-ms first (raw/github/typesafe-sdk-js/src/retry.ts, raw/github/typesafe-sdk-python/src/typesafe_sdk/_core/errors.py). A negative or unparseable value yields no delay and the client falls back to backoff. In the JS SDK a server delay is only honored when it is <= maxRetryAfterMs (default 60,000 ms); otherwise backoff is used.

Default retry policies

Setting Python RetryPolicy JS RetryPolicy
Max retries after the initial attempt (0 disables) max_retries = 2 maxRetries = 2
First backoff delay backoff_initial = 0.5 (seconds) backoffInitialMs = 500
Backoff cap backoff_max = 5.0 (seconds) backoffMaxMs = 5000
Jitter (fraction subtracted) backoff_jitter = 0.25 backoffJitter = 0.25
Retryable statuses http_statuses = {408, 429, *range(500, 600)} httpStatuses = {408, 429, 500–599}
Honor retry-after headers respect_retry_after = True respectRetryAfter = true
Max honored server delay — (not present) maxRetryAfterMs = 60000
Retry connection errors api_connection_error = True apiConnectionError = true
Retry timeouts api_timeout_error = True apiTimeoutError = true
Extra retryable exception types exceptions, a set of exception types, default empty
Custom predicate predicate, a callable taking the raised exception and returning bool, default None
Overall retry deadline timeout: float | None = 30.0
Per-request HTTP timeout DEFAULT_TIMEOUT = 10.0 seconds (raw/docs/sdk__python__api__constants.md) DEFAULT_TIMEOUT_MS = 10_000 (raw/github/typesafe-sdk-js/src/retry.ts)

Both defaults cover 529 because it falls inside 500–599.

Delay formula used by the JS SDK (raw/github/typesafe-sdk-js/src/retry.ts), for a zero-based attempt index: if a server delay is present and within maxRetryAfterMs, use it; otherwise round(min(backoffInitialMs * 2**attempt, backoffMaxMs) * (1 - random() * backoffJitter)). That gives roughly 375–500 ms, then 750–1000 ms, then 1500–2000 ms.

Overriding the policy (Python, verbatim from raw/docs/sdk__python__api__retries.md)

from typesafe_sdk import RetryPolicy, TypeSafeClient

client = TypeSafeClient(
    retry=RetryPolicy(
        max_retries=3, timeout=10.0, http_statuses={429, 500, 502, 503, 504}
    )
)

Note this override drops 408 and the rest of the 5xx range, including 529. Add them back if you want the documented overload behavior.

Recommended client behavior if you are not using an SDK

  1. Retry only 408, 429, and 5xx. Everything else is a permanent failure for that request.
  2. Read retry-after-ms first, then retry-after; cap any honored delay (60 s is the SDK default cap).
  3. Otherwise back off exponentially from 500 ms, doubling to a 5 s cap, subtracting up to 25% jitter.
  4. Stop after 2–3 retries and surface the error with the x-typesafe-request-id.
  5. On 422, parse detail[] and log loc joined by . with msg — this is exactly what both SDKs do to build the error message. See OpenAPI component schemas.
  6. On 401, do not retry and do not rotate through keys automatically.

Version notes

Related

Sources