Errors reference#
An index: which class is raised for which wire error, and which HTTP status each error type carries on the hosted and local drivers. Symptom, cause, and fix for each message the ask loop and the observe can meet live in the troubleshooting guide; the hosted plane’s pre-authentication and framework refusals (408 request_timeout, 413 payload_too_large, http_error) and the observation cap (409 observation_cap_reached) have no engine twin; their symptom text lives in the service’s own error table on the REST API page. The hosted rows follow the service at its 74e0202 build, the one prod serves (the REST reference’s dump).
Exception classes#
Class |
Bases |
Raised when |
|---|---|---|
|
|
A non-2xx response from the hosted service, or the local driver’s equivalent refusal. |
|
|
Never raised bare; the catch-all base for terminal states. |
|
|
A wire error whose |
|
|
|
|
|
|
|
|
|
|
|
The caller’s argument error at the ask boundary ( |
ApiError attributes: status_code (int), error_type (str), message (str), details (dict or None), headers (the final response’s headers; an empty mapping from the local driver), and the property retry_after – Retry-After parsed as seconds, None when the header is absent, not a plain number, negative, or non-finite (the HTTP-date form is not parsed). str(error) is [<status_code>] <error_type>: <message>.
Wire-to-class mapping (hosted driver)#
The transport maps the final non-2xx response in this order:
error_typespace_exhausted->SpaceExhaustederror_typeunique_candidate_unavailable->ApiError, even when a terminal marker is present (no proof, so never completion)error_typeask_deadline_exceeded->AskDeadlineExceededwhendetailscarries the engine’s four-key receipt (every key present;pointisrung_boundary,deliveryorbackstop,elapsed_sa number,max_wait_sa number or null,runga string or null; abackstopbody must also carrystage, a string, andbackstop_s, a number –backstop_sourcea string, null, or absent – and maps ontoBackstopExceeded), elseApiError(never a guessed receipt); the type is handled whole here, so step 4 never sees it – a refusal is never completion, whatever marker a stray body carriesdetails.terminalistrue->TerminalApiErroranything else ->
ApiError
A body that is not JSON maps to ApiError with error_type api_error and the first 500 characters of the body (or the reason phrase) as the message. A 3xx response raises ApiError with error_type unexpected_redirect before any retry; the transport never follows redirects. Connection-level failures (httpx.ConnectError, httpx.ReadTimeout, and their siblings) raise the raw httpx exception and are not retried.
Retries happen before the mapping: statuses 429, 500, 502, 503, and 504 are retried on non-POST requests; a POST retries a 429 and a 503 whose body’s error_type is in the transport’s PRE-COMMIT set – experiment_busy, queue_full, and ask_slots_busy, retired at deploy 2 with the lane it guarded but kept in the set – each a refusal before any suggestion or observation is committed, with Retry-After – and never any other 5xx (it may follow a server-side commit: a 503 without the JSON shape, ask_deadline_exceeded, suggest_timeout and connection_pool_busy raise at once – the pooled connection is released at commit, so a post-commit read can answer connection_pool_busy for a row already written); there are at most 5 attempts in total. The delay is Retry-After when present (capped at 8 seconds), otherwise 0.5 * 2^attempt seconds capped at 8.
HTTP status by error type#
Status |
|
Class |
Hosted |
Local |
|---|---|---|---|---|
400 |
|
|
yes |
yes |
401 |
|
|
yes |
no |
403 |
|
|
yes: the admin API’s door only ( |
no |
403 |
|
|
yes: the web app’s sign-in and session routes (an |
no |
404 |
|
|
yes |
yes |
408 |
|
|
yes (the request body was not received within 30 s) |
no |
409 |
|
|
yes |
yes |
409 |
|
|
yes, with |
no |
409 |
|
|
yes |
yes |
409 |
|
|
yes, with |
no (the local driver never expires a suggestion) |
409 |
|
|
yes: a rotation of an expired token on the web app’s tokens page (revoke it and mint anew); a session route, never answered to a token |
no |
409 |
|
|
yes |
yes |
409 |
|
|
yes, no Retry-After: an |
no |
413 |
|
|
yes (a request body past 1048576 bytes) |
no |
429 |
|
|
yes, Retry-After 1 |
yes, no header ( |
429 |
|
|
yes ( |
no |
429 |
|
|
yes, Retry-After 1: more than eight requests in flight from one token, more than 40 a second (burst 80), or more than 15 history-sized reads a second (burst 30); answered before the request is read, reads and observations included |
no |
429 |
|
|
yes, Retry-After 1, with |
no |
429 |
|
|
yes, Retry-After 1, with |
no |
429 |
|
|
yes, Retry-After 1, with |
no |
429 |
|
|
yes: a mint past the plan’s live-token cap per member (5 on the beta; revoke one first); a session route on the web app’s tokens page, never answered to a token |
no |
503 |
|
|
yes, no Retry-After, with |
yes, no header, with |
503 |
|
|
yes, Retry-After 1 (another request holds the experiment’s lock and the 3 s bounded wait expired); retried by the transport on every method, a POST included (a pre-commit refusal) |
no (one process, no lock) |
503 |
|
|
yes, with Retry-After |
yes, no header |
503 |
|
|
yes, Retry-After 1 (every suggest permit of the instance held, the ask never entered the queue; no pooled database connection within the checkout wait – the checkout failed, which can happen after an earlier statement of the same request committed) – |
no |
500 |
|
|
yes, REDACTED: a constant message per type, and |
yes, with the engine’s full |
500 |
|
|
yes |
yes |
404, 405, 400 |
|
|
yes: the framework’s own refusal inside the envelope (a path that is no route, a method the route lacks, a body the framework cannot parse) |
no |
403 |
|
|
yes, on the web app’s session routes only (the sign-in bootstrap, the token management and the sign-in-methods sync): a verified identity whose account is not active, a session request without an allowed |
no |
3xx |
|
|
raised client-side |
no |
any non-2xx |
|
|
raised client-side for a non-JSON body |
no |
The local driver (Client(driver="local")) raises its rows with the same status codes and error_type values the service sends (message text differs on 503, 404, 409 conflict, and the local driver’s own 400 range checks on the v0 integer fields, which name the rule – observation_budget must be >= 1 – where the service’s pydantic text names the field; the suggestion_ttl_s door answers the service’s own text, suggestion_ttl_s: Input should be greater than or equal to 0), so one handler keyed on error_type serves both drivers. The three 500 types are the engine’s own failures (the AskFailed and ValueError rows of the next section): all_rungs_failed and integrity_violation carry the engine error’s details (the attempts, or the failed check), internal_error carries details.exception_type. A missing, malformed, unknown, revoked, or other-lane token answers 401 unauthorized (message missing Bearer token or invalid token); the service’s 403s are forbidden (the admin API’s door, /admin/api/*, never a /v1 route), origin_refused (the pre-authentication gate’s Origin check) and account_pending / account_disabled (the web app’s sign-in). The Authorization scheme match is case-insensitive on every door and the separator is a single space: Authorization: Bearer <token> and bearer <token> are both READ as the Bearer scheme (the token is then looked up; the admin API’s door refuses any token with its own 403), while a header without the bearer prefix (Bearer<token>, a tab, another scheme) answers 401 unauthorized with the message missing Bearer token on the /v1 doors (the admin API’s own door, outside this page, says missing credential), the scheme match failing before any lookup (fail-closed; the Python client sends Bearer <token>). The MCP server’s own boundary accepts the scheme the same way before forwarding.
Engine-level exceptions#
Raised by tauopt.ask(state, config=...) and the strategies; the drivers map some of them.
Exception |
Module |
Raised when |
Through the local driver |
|---|---|---|---|
|
|
a rung overran its wall budget and |
503 |
|
|
|
raised unchanged: it already is the 503 |
|
|
every rung in the fallback chain failed, the stdlib-only |
500 |
|
|
an integrity postcondition failed after a rung answered, deliberately outside the never-fail ladder: the message starts with |
500 |
|
|
a strategy’s dedup repair ran out, or a queue-selection config met a strategy that cannot queue |
caught inside the ask boundary; never reaches callers |
|
|
a bad |
400 |
|
config, space, state, and observation validation |
a malformed config, space, metric, intent, or observation, refused at the driver’s door before the ask runs |
400 |
any other |
the engine, inside |
the engine’s own fault (a record that failed its validator); none is reachable today |
500 |
|
|
|
not reachable |
|
|
|
raised unchanged |
The hosted service maps TimeboxExceeded, AskValidationError, AskFailed, and the other ValueError the same way (the mapping that ships with this engine version; see the note under the status table), and AskDeadlineExceeded to 503 ask_deadline_exceeded with the receipt in details and no Retry-After, which the hosted driver reads back onto the AskDeadlineExceeded class, so except AskDeadlineExceeded and except ApiError both see the refusal on both drivers.