Errors
Error codes and how to handle them.
Errors the gateway itself generates use a single JSON envelope. Errors from an upstream provider (a 4xx/5xx the provider returned) are forwarded as-is, in that provider's own shape, and are not wrapped in this envelope.
{
"error": {
"code": "wesence.insufficient_credits",
"message": "insufficient credits",
"request_id": "018f2c3e-1234-7890-abcd-ef0123456789"
}
}Always branch on error.code, not just the HTTP status — several distinct
conditions share the same status code (three different codes all return
402, for example).
try {
await client.chat.completions.create({ model: 'openai/gpt-4o-mini', messages: [...] });
} catch (err) {
if (isInsufficientCreditsError(err)) {
// out of platform credit — top up
} else if (isProviderError(err)) {
// every candidate upstream provider failed
}
}Codes
| Code | HTTP status | Meaning |
|---|---|---|
wesence.bad_request | 400 | Malformed request the gateway rejected before dispatch — missing model, invalid JSON, an unknown provider prefix, a malformed @slug/model, etc. |
wesence.invalid_key | 401 | Missing, malformed, unknown, or expired API key / token |
wesence.revoked_key | 401 | The API key or ephemeral token was revoked |
wesence.scope_denied | 403 | An ephemeral token's scope doesn't permit this route or model |
wesence.misdirected_region | 421 | The key is bound to a different gateway region than the one that received the request; the error body names the correct host |
wesence.insufficient_credits | 402 | Workspace platform credit balance is exhausted |
wesence.credential_cap_exceeded | 402 | A specific BYOK credential's (or ephemeral token's) own spend cap is exhausted — other credentials on the same tenant may still work |
wesence.budget_exhausted | 402 | A configured key/connection/workspace budget was hit |
wesence.guardrail_denied | 422 | A guardrail configured to deny failed, on input or output |
wesence.rate_limit | 429 | A rate limit was exceeded |
wesence.provider_error | 502 / 503 | Every routing candidate's upstream call failed, or the resolved provider isn't wired on this deployment |
wesence.internal | 500 | Unexpected gateway-side failure |
request_id on the error body matches the X-Request-ID response header,
present on every response including errors — include it when reporting an
issue.
Retrying
Only 429 carries a Retry-After header telling you how long to wait.
Beyond that, there's no AI Setu-specific retry convention — apply standard
HTTP judgment: a 5xx/502/503 is generally safe to retry with backoff,
a 4xx (other than 429) means the request itself needs to change first.
Failover already retries transient upstream
failures across providers automatically before returning an error to you —
if you're seeing wesence.provider_error, every candidate has already
failed.