Skip to content

Errors

Guides

Errors

The error envelope, every Tokentide error code, upstream errors and when to retry.

Tokentide’s own errors use the OpenAI-compatible envelope, so the OpenAI SDKs (and anything built on them) raise them as normal API errors with a code:

Error body
{
  "error": {
    "message": "Low Tide is closed right now. It is forecast to open at 19:40 UTC.",
    "type": "tokentide_error",
    "code": "lowtide_closed",
    "param": null,
    "request_id": "req_01JDV6Q7Z8R2M3N4P5Q6R7S8T9"
  }
}
  • type is always tokentide_error for errors Tokentide generates. Upstream errors that Tokentide passes through keep Venice’s body (see Upstream errors).
  • code is stable; match on it, not on message.
  • param names the offending field or header when there is one.
  • request_id equals the X-Request-Id header. Quote it to support.
  • Retry-After (seconds) is set whenever waiting will help.

Error codes

Every code, its HTTP status and what to do. Link to a row with its code, for example /docs/errors#lowtide_closed.

Tokentide error codes with their HTTP status, when they happen, whether to retry and what to do
CodeHTTPWhenRetry?What to do
invalid_request400Tokentide validation failed: a bad header value, query or slot request, a field over a model limit (for example max_completion_tokens), TTS input over 4,096 characters, image variants outside 1–4, an invalid X-Tokentide-UploadNoFix the request; param names the field
lowtide_requires_non_streaming400X-Tokentide-Lane: lowtide with stream: true (or TTS with streaming: true)NoSend stream: false, or use the Standard lane
invalid_api_key401Missing, malformed, unknown, revoked or expired key; a Venice key; a tt_live_ key outside production or a tt_test_ key in productionNoSend Authorization: Bearer tt_live_… with an active key
unauthorized401The credentials are valid but cannot be used for this routeNoUse the credential type the route expects
insufficient_balance402Your balance cannot cover the request’s worst-case cost (reserved at admission, refunded down to the actual charge afterwards), or a Large Payload Lane upload slot was requested with no spendable balanceAfter topping upTop up, or lower max_completion_tokens to shrink the reservation
spend_limit_reached402A daily, weekly or monthly spend cap you set on the key or account was reached (or is 0, which blocks all spend), or this request’s worst-case cost is more than the room left under itAfter the cap resets (Retry-After; the message names the reset time), or after 60 seconds when only the request was too largeRaise the cap in the dashboard, wait for the next day, week or month, or lower max_completion_tokens
model_not_allowed403The model’s privacy tier exceeds your key’s privacy mode, the model is not offered, or a request on the Low Tide lane names a model excluded from Low TideNoUse an allowed model, or a key with privacy mode all for anonymized models; for Low Tide, use the Standard lane or send X-Tokentide-Lowtide-Fallback: standard
ip_not_allowed403The key has an IP allowlist and the request came from another addressNoCall from an allowed address or update the allowlist
account_restricted403The account is suspended, banned or closedNoContact support@tokentide.app
forbidden403You are authenticated but may not access this resourceNoAccess only your own resources
not_found404Unknown route or resource (including Venice account routes, which are never proxied, and unknown or expired uploads)NoCheck the path or id
method_not_allowed405The route exists but not with this HTTP methodNoUse the documented method (see the Allow header)
price_above_limit409The price would exceed X-Tokentide-Max-Multiplier (or the key’s max multiplier), including a cross-epoch upgrade near midnight UTCYes, after Retry-AfterWait until the table crosses below your limit, or raise the limit
cost_above_limit409The worst-case charge could exceed X-Tokentide-Max-Cost-USDNo (same request)Lower max_completion_tokens or raise the limit
lowtide_closed409Low Tide is closed (no spare capacity, congestion, the last 2 minutes before the reset, or paused)Yes, after Retry-AfterRetry at X-Tokentide-Lowtide-Opens-At when present, or send X-Tokentide-Lowtide-Fallback: standard
lowtide_cap_reached409Your hourly Low Tide cap or daily Low Tide allowance is used upHourly cap: after Retry-AfterUse the Standard lane (or the fallback header); the allowance grows with Standard usage
duplicate_request409The Idempotency-Key was already accepted in the last 24 hoursNoTreat the earlier request as the one that ran; results are never replayed (check GET /v1/account/usage)
payload_too_large413The body exceeds the 4.5 MB direct limit, or an upload exceeds its declared or allowed sizeNoUse a public URL input or the Large Payload Lane
unsupported_media_type415The Content-Type or encoding is not accepted on this routeNoSend application/json (or multipart/form-data where documented)
rate_limited429One of our limits: requests per minute per key, concurrent requests, spend velocity, image requests per minute, market stream connections, upload-slot requests, model congestion shedding, or the 4xx circuit breakerYes, after Retry-AfterBack off with jitter
internal_error500An unexpected error on our sideYes, with backoffRetry with an Idempotency-Key; quote the request id if it persists
upstream_unavailable502Venice failed or was unreachable after our retry policy, or a non-streaming response was larger than the 64 MB Tokentide relaysYes, with backoffRetry later; check status.tokentide.app. A response over 64 MB fails again: use stream: true or a smaller request
capacity_unavailable503Today’s discounted capacity is sold out (GET /v1/market shows sold_out: true and reopens_at), sales are paused, too many requests that may run past the daily reset are in flight, or the market is briefly unavailableYes, after Retry-AfterWhen sold out, Retry-After counts down to the daily reset at 00:00 UTC, when sales reopen. Otherwise retry later
epoch_rollover503The daily reset is in progress: image endpoints and quotes wait for the new dayYes, after Retry-AfterRetry after Retry-After (usually under a minute)
service_unavailable503A feature is temporarily unavailable, for example the Large Payload Lane not enabledYes, laterRetry later
upstream_timeout504Venice did not answer in time (mostly long non-streaming requests)Not blindlyUse stream: true for long generations. The request may have run upstream: if Venice had already answered, it is charged as reserved (never more) and trued up to what Venice billed when the response carries its id

Upstream errors

Validation errors from Venice are passed through with their status and body, because clients expect Venice semantics there: 400, 404, 413, 415 and 422 (including the OpenAI-style context_length_exceeded). Their body is Venice’s ({"error": "…"}, sometimes with details and issues, or {"error": {…}} for context-length errors), not the Tokentide envelope.

Some upstream errors are never passed through, because they concern our supply, not your request:

Venice responseYou receive
402 (supply balance or key spend limit)503 capacity_unavailable
401 / 403 caused by Tokentide’s own key502 upstream_unavailable
403 because of the model’s privacy tier403 model_not_allowed
429 (rate limit or model overload)429 rate_limited with Retry-After
500 / 502 / 503 before any byte was streamedretried by Tokentide, then 502 or 503
504504 upstream_timeout (not retried)

Images flagged by Venice as content violations come back as 200 with x-venice-is-content-violation: true and are not charged. 422 content-policy rejections are normally not charged either.

Errors inside a stream

Once a stream has started, the HTTP status is already 200. If the upstream fails after that, the error arrives in-band, passed through untouched, followed by the terminator:

Error inside a stream
data: {"error":{"message":"…","type":"server_error","code":"model_overloaded","param":null,"retry_after":30}}
 
data: [DONE]

Treat an in-band error as terminal. Tokentide never retries a request whose stream has started. If a stream ends without its final usage chunk (an in-band error, a dropped connection or your own disconnect), the request is settled provisionally at its worst-case cost, never more than was reserved for it, and trued up against Venice’s own ledger for that request: anything Venice did not bill is credited back automatically.

The same applies to a non-streaming response that Venice answered but that could not be delivered to you (the connection was cut, the request ran out of time, or the body is larger than the 64 MB Tokentide relays without streaming). Venice has already billed it, so it is settled provisionally at its worst-case cost, never more than was reserved, and trued up against Venice’s ledger when the response carries Venice’s request id (chat completions and responses). Such a request keeps its Idempotency-Key: a retry with the same key answers 409 duplicate_request. Embeddings batches whose vectors would exceed 64 MB are refused up front with 400 invalid_request (param: "encoding_format"): send "encoding_format": "base64", a lower dimensions (on models that support it) or a smaller batch.

Retry rules

  • Never retry unchanged: 400, 401, 403, 404, 405, 413, 415, 422. Repeating them cannot succeed, and repeated client errors on one model trip a protective breaker: more than 10 upstream 4xx in 30 seconds from one customer answers 429 rate_limited for 30 seconds (this protects the shared upstream error budget).
  • Retry after Retry-After: 409 price_above_limit, lowtide_closed, lowtide_cap_reached, 429, 503.
  • Retry with exponential backoff and jitter (0.5 s, 1 s, 2 s, 4 s … capped at 30 s, 3–5 attempts): 500, 502.
  • 402 is about money, not load: insufficient_balance clears only when your balance grows (top up, or turn on auto top-up); spend_limit_reached clears when the cap resets (Retry-After) or when you raise it; when only that request was too large for what is left, smaller requests still pass and Retry-After is 60 seconds. See Handling 402 errors.
  • Use Idempotency-Key on retries of requests that may have been processed. A duplicate answers 409 duplicate_request instead of running twice.

Debugging a request

Every response, successful or not, identifies the request:

  • X-Request-Id (also request_id in the error envelope) is our id for the request. Quote it to support. Once the request has settled, the dashboard’s Request Inspector shows its metadata.
  • X-Client-Request-Id is yours: send a correlation id (1–128 characters of letters, digits and . _ : / + = -, for example a UUID) and it is echoed back. We record only a keyed hash of it, never the id itself, so it can hold your own ids safely. It matters when a call timed out or failed before any response arrived, so you never saw an X-Request-Id: support can still find the request by your id. Other values are ignored, never rejected. The Tokentide SDKs send a random one with every request and print it in their errors.
  • Public answers our CDN caches (GET /v1/market, /v1/status, /v1/openapi.json, /v1/image/styles, and /v1/models with its /traits and /compatibility_mapping without credentials) never echo X-Client-Request-Id: a cached answer is served to every caller. Their X-Request-Id can be the one of the request that filled the cache.

Add X-Tokentide-Debug: 1 to see where the time went and what the upstream answered. Nothing extra is stored. Error responses carry these headers too, for the stages the request reached before it failed:

HeaderMeaning
X-Tokentide-Debug-TimingsServer-Timing syntax, in ms: auth, admit (admission without lease work), lease (refilling your credit lease), upstream_ttfb (the upstream’s own time to first byte), overhead (latency Tokentide added before it) and total
X-Tokentide-Debug-Upstream-StatusThe HTTP status the upstream answered
X-Tokentide-Debug-RetriesUpstream retries (connection retries and failover between upstream keys)
X-Tokentide-Debug-Upstream-IdThe upstream response id (non-streaming responses)
x-venice-version, cf-rayUpstream version and ray id

When you contact support, include the X-Request-Id (or your X-Client-Request-Id), the time in UTC, the host you called and the error code. Never send prompts or outputs: metadata is all we need, and all we keep.

Reading errors with the OpenAI SDKs

import openai
 
try:
    client.chat.completions.create(
        model="deepseek-v4-1-flash",
        messages=[{"role": "user", "content": "…"}],
        extra_headers={"X-Tokentide-Lane": "lowtide"},
    )
except openai.APIStatusError as e:
    print(e.status_code, e.code, e.response.headers.get("retry-after"))
    print(e.response.headers.get("x-tokentide-lowtide-opens-at"), e.request_id)

The Tokentide SDKs raise a typed error with the code, Retry-After and the parsed X-Tokentide-* headers. See SDK helpers.