Error Codes
Most Olyx responses that indicate a failure use an error string. Validation-heavy control-plane endpoints may return
an errors array when multiple fields failed.
{ "error": "Description of what went wrong" }
{ "errors": ["Name can't be blank"] }
Errors raised by gateway protection mechanisms include a machine-readable code field alongside the human-readable message:
{ "error": "Kill switch engaged: hourly limit of $5.00 exceeded.", "code": "CIRCUIT_OPEN" }
Two response patterns are not errors even though they might look like one at first glance:
- Blocked requests return
200with areasonfield and nooutput. The request succeeded — a safety or routing rule decided not to forward it. - Tool calls pending return
200withstatus: "tool_calls_pending". The model is waiting on tool results — this is part of the normal agentic flow.
Both are described at the bottom of this page.
HTTP Status Codes
| Status | When it occurs |
|---|---|
400 Bad Request | A required parameter is missing or the request body is malformed. |
401 Unauthorized | Token is missing, invalid, expired, or the API key has been revoked or paused. |
402 Payment Required | Your project’s monthly budget cap has been reached. |
404 Not Found | The resource does not exist or belongs to a different account. |
422 Unprocessable Entity | The request is well-formed but failed validation (e.g. a required field is blank). |
429 Too Many Requests | Rate limit exceeded, or a per-key hourly spend cap has been hit. |
500 Internal Server Error | Unexpected server error — safe to retry with exponential back-off. |
Auth Errors (401)
error value | Cause and fix |
|---|---|
"Unauthorized" | API key is missing, revoked, or the secret portion is wrong. Check your key in Settings. |
"Token expired" | Session cookie is stale or invalidated. Sign in again. |
"Invalid token" | Session cookie state is invalid. Sign in again. |
Budget Errors (402)
error value | code | Cause and fix |
|---|---|---|
"Monthly budget exceeded." | BUDGET_EXCEEDED | Your project’s monthly spend cap was reached. Increase the budget in Settings or wait for the next billing period. |
Rate & Spend Errors (429)
error value | code | Cause and fix |
|---|---|---|
"Rate limit exceeded" | — | Key has made 1,000+ requests in the current rolling hour. Wait for the window to reset. |
"Kill switch engaged: hourly limit of $X exceeded." | CIRCUIT_OPEN | Key’s hourly spend cap was reached. Key is paused — reset it in Settings → API Keys once the issue is resolved. |
"Recursive loop detected. Account throttled." | LOOP_DETECTED | More than 50 requests in 10 seconds — Olyx detected a likely runaway loop and paused the key. |
Validation Errors (422)
error value | Cause |
|---|---|
"Name can't be blank" | A required name field was missing on create or update. |
"Model not authorized or found: gpt-x" | The model identifier isn’t registered in the Model Registry for this project. |
"Trace not found or not owned by this customer" | The trace_id doesn’t exist or belongs to a different account. |
Blocked Requests (200 with reason)
A blocked response is not an HTTP error — it returns 200 with a reason field and no output. The request reached Olyx successfully; the safety or routing layer decided not to forward it to a model.
{ "reason": "No private model configured for sensitive routing", "step_id": 44 }
Common reasons:
reason | Cause |
|---|---|
"Request blocked by risk check" | Input contained high-risk terms detected by the guardrail. |
"No private model configured for sensitive routing" | PII or confidential data detected but no private model is registered in the Secure tier. |
"No model configured — set routing tiers or fallbacks in project settings" | No model could be resolved for the routing tier. |
Blocked steps are recorded in the trace with type: "blocked" and the reason string, visible in the execution graph on the dashboard.
Tool Calls Pending (200 with status)
When a model requests tool calls, the response is also 200 — not an error — with status: "tool_calls_pending":
{
"tool_calls": [{ "id": "call_1", "name": "get_weather", "arguments": { "city": "London" } }],
"step_id": 43,
"status": "tool_calls_pending"
}
Execute each tool locally or via your MCP server, then call POST /api/v1/executions again with tool_results and parent_step_id to continue. See Executions for the full flow.
404 Scoping
404 is returned when a resource either doesn’t exist or belongs to a different account. This is intentional — your application cannot determine whether a resource exists across account boundaries.
Retry Guidance
| Status | What to do |
|---|---|
401 | Check or rotate your API key. Re-authenticate if using a dashboard session. |
402 | Your project’s monthly budget cap was hit. Raise the limit in Settings or wait for the next period. |
404 | Verify the resource ID and that you’re using the correct API key for the project. |
422 | Fix the request — do not retry with the same payload. |
429 (rate limit) | Back off and retry after the current hour window resets. |
429 (CIRCUIT_OPEN) | Check your key’s spend in the dashboard, fix the issue, then reset the key. |
429 (LOOP_DETECTED) | A runaway loop was detected and the key was paused. Investigate the loop, then reset the key. |
500 | Retry with exponential back-off (e.g. 1s, 2s, 4s). Contact support if it persists. |
Ruby SDK Errors
When using the Ruby SDK, HTTP errors are translated into typed Ruby exceptions.
| Class | Trigger |
|---|---|
Olyx::AuthError | 401 |
Olyx::NotFoundError | 404 |
Olyx::ValidationError | 422 |
Olyx::RateLimitError | 429 — check .code: "CIRCUIT_OPEN" (spend cap) or "LOOP_DETECTED" (runaway loop) |
Olyx::ServerError | 5xx from the gateway |
Olyx::GatewayError | Network timeout, connection refused, or 5xx — internal signal that triggers the fail-open/closed decision |
Olyx::CircuitBreakerError | Gateway unreachable and fail_open is false. Request blocked — no ungoverned path to the LLM provider |
Olyx::ConfigurationError | Invalid SDK configuration (missing key, invalid fail-open config, etc.) |
CircuitBreakerError is the SDK’s primary fail-closed behavior: when the governed path is unavailable and the caller has not explicitly opted into fail_open, no prompt reaches a provider without Olyx’s PII scrubbing and audit trail.
begin
result = client.execute(trace_id: trace.id, input: "...")
rescue Olyx::CircuitBreakerError => e
# Gateway unreachable, fail-closed. Nothing was sent to the provider.
render json: { error: "AI service temporarily unavailable." }, status: :service_unavailable
rescue Olyx::RateLimitError => e
case e.code
when "CIRCUIT_OPEN" then :reset_key_in_dashboard
when "LOOP_DETECTED" then :investigate_loop_then_reset_key
else :wait_for_window_reset
end
rescue Olyx::AuthError
# Rotate key in Settings → API Keys
end
See the Ruby SDK error reference and Safety Valve for the full fail-open/closed behaviour.
Python SDK Errors
When using the Python SDK, HTTP errors are translated into typed Python exceptions.
| Class | Trigger |
|---|---|
olyx.AuthError | 401 |
olyx.NotFoundError | 404 |
olyx.ValidationError | 422 |
olyx.RateLimitError | 429 — check .code: "CIRCUIT_OPEN" (spend cap) or "LOOP_DETECTED" (runaway loop) |
olyx.ServerError | 5xx from the gateway |
olyx.GatewayError | Network timeout, connection refused, or 5xx — internal signal that triggers the fail-open/closed decision |
olyx.CircuitBreakerError | Gateway unreachable and fail_open is False. Request blocked — no ungoverned path to the LLM provider |
olyx.ConfigurationError | Invalid SDK configuration (missing key, invalid fail-open config, etc.) |
CircuitBreakerError is the SDK’s primary fail-closed behavior: when the governed path is unavailable and the caller has not explicitly opted into fail_open, no prompt reaches a provider without Olyx’s PII scrubbing and audit trail.
try:
result = client.execute(trace_id=trace.id, input="...")
except olyx.CircuitBreakerError:
# Gateway unreachable, fail-closed. Nothing was sent to the provider.
return {"error": "AI service temporarily unavailable."}, 503
except olyx.RateLimitError as e:
if e.code == "CIRCUIT_OPEN":
pass # reset key in dashboard
elif e.code == "LOOP_DETECTED":
pass # investigate loop, then reset key
else:
pass # wait for window to reset
except olyx.AuthError:
pass # rotate key in Settings → API Keys
See the Python SDK error reference and Safety Valve for the full fail-open/closed behaviour.
TypeScript SDK Errors
When using the TypeScript SDK, HTTP errors are translated into typed JavaScript errors.
| Class | Trigger |
|---|---|
AuthError | 401 |
NotFoundError | 404 |
ValidationError | 422 |
RateLimitError | 429 — check .code: "CIRCUIT_OPEN" or "LOOP_DETECTED" |
ServerError | 5xx from the gateway |
GatewayError | Network timeout, connection refused, or gateway transport failure |
CircuitBreakerError | Gateway unreachable and failOpen is false |
ConfigurationError | Invalid SDK configuration |
import { AuthError, CircuitBreakerError, RateLimitError } from "@olyx-labs/olyx";
try {
const result = await client.execute({ traceId: trace.data.id, input: "..." });
} catch (err) {
if (err instanceof CircuitBreakerError) {
res.status(503).json({ error: "AI service temporarily unavailable." });
} else if (err instanceof RateLimitError) {
if (err.code === "CIRCUIT_OPEN") {
// Review spend, then reset the key.
}
if (err.code === "LOOP_DETECTED") {
// Investigate the loop, then reset the key.
}
} else if (err instanceof AuthError) {
// Rotate key in Settings -> API Keys.
} else {
throw err;
}
}
See the TypeScript SDK error reference and Safety Valve for SDK-specific fail-open/closed behavior.