Skip to main content
When something fails, start with the exception class. If you need a symptom-based guide, use Troubleshooting. All Tex exceptions inherit from tex.TexError. Most apps catch one of these:
ClassStatusInheritsWhen
BadRequestError400APIStatusErrorMalformed payload
AuthenticationError401APIStatusErrorBad / expired key
PermissionDeniedError403APIStatusErrorKey revoked or lacks scope
NotFoundError404APIStatusErrorUnknown resource
ConflictError409APIStatusErrorResource exists (e.g. duplicate org_id)
UnprocessableEntityError422APIStatusErrorPydantic validation failure
RateLimitError429APIStatusErrorDaily quota exceeded
InternalServerError5xxAPIStatusErrorOur problem; SDK retried
APITimeoutErrorAPIConnectionErrorAPIErrorNetwork or server too slow
APIConnectionErrorAPIErrorDNS, TLS, connection reset
[APIResponseValidationError]-APIErrorServer returned an unexpected response
TexHTTPError (alias of APIStatusError) and TexAuthError (alias of AuthenticationError) are kept for backward compatibility.

Common fields

# All TexError subclasses
e.message         # human-readable

# APIStatusError subclasses (everything with an HTTP status)
e.status_code     # int
e.request_id      # X-Correlation-ID; include this in support tickets
e.details         # dict; server JSON, may include field errors
e.response_text   # raw response body, capped at 2KB
APITimeoutError and APIConnectionError are network errors, not HTTP errors. They do not have status_code, request_id, details, or response_text because the request never produced a response. Catch them separately.
from tex import Tex, RateLimitError, AuthenticationError, APITimeoutError, BadRequestError

try:
    tex.recall(q=q, session_id=sid)
except RateLimitError:
    return cached_or_fallback()
except AuthenticationError:
    page_oncall("tex auth broken")
    raise
except APITimeoutError:
    return degraded_no_memory_response()
except BadRequestError as e:
    log.warning("bad payload: %s", e.details)
    raise

Per-class details

BadRequestError

Raised when a payload is malformed. Common causes:
  • Missing required field on a turn (e.g. no text)
  • Invalid mode value on recall
  • Invalid session_id value. It must be a string.
e.details includes a Pydantic-style loc list that points to the bad field.

AuthenticationError

Status 401. The SDK already tried one JWT refresh before raising.
try:
    tex.recall(q=q, session_id=sid)
except AuthenticationError as e:
    if "Invalid API key" in e.message:
        # Bad API key
        rotate_key_alarm()
    else:
        # JWT refresh failed, likely revoked
        notify_user("please log in again")

PermissionDeniedError

Status 403. The credential is valid but lacks scope. This mostly matters for scoped keys. Default keys should not hit this.

NotFoundError

Status 404. You referenced something that does not exist. This is often a stale key_id on DELETE /me/api-keys/{id}.

UnprocessableEntityError

Status 422. FastAPI validation rejected the payload. The SDK builds payloads for you, so this usually means an argument has the wrong type.

RateLimitError

Status 429. The SDK retries on 429 like other transient codes (with exponential backoff and Retry-After honored), so by the time you see this exception the SDK has already exhausted retries. For daily-quota 429s, retries will not help until midnight UTC. Set max_retries=0 on paths where you would rather fail fast:
try:
    tex.recall(q=q, session_id=sid)
except RateLimitError as e:
    return generate_without_memory(q)   # graceful degradation
The e.details payload tells you which cap was exceeded (tokens_in_daily or tokens_out_daily) and when it resets:
e.details
# {"error":"quota_exceeded","kind":"tokens_in_daily","used":1000123,"limit":1000000,"period":"day_utc","period_start":"2026-05-08T00:00:00+00:00","message":"…"}

InternalServerError

Status 5xx. The SDK already tried the request again with exponential backoff. If you still see this, file a ticket with e.request_id.

APITimeoutError

The request did not return within timeout. The SDK retries timeouts. If every attempt fails, it raises APITimeoutError.
try:
    hits = tex.recall(q=q, session_id=sid)
except APITimeoutError:
    hits = None   # fall back to no-memory generation

APIConnectionError

DNS, TLS, or socket-level failure. The retry behavior is the same as APITimeoutError. If you see this in production, check your egress proxy or firewall.

Built-in retries

The SDK retries automatically on:
  • Status codes: 408, 429, 500, 502, 503, 504
  • httpx.TimeoutException
  • httpx.HTTPError (network)
Default: 2 retries with exponential backoff (0.5s, 1s). Override:
tex = Tex(api_key=..., max_retries=5)
The SDK honors Retry-After. If the server says wait 3 seconds, the SDK waits at least 3 seconds.
Quota 429s retry like other 429s. The retry will still fail if you are over the cap. Set max_retries=0 on quota-sensitive paths if you want to fail faster.

Idempotency

remember is idempotent because Tex deduplicates turns by hash. recall and usage.* are read-only. It is safe to retry any of them.

Next: REST API

Direct HTTP integration without the SDK.