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 200 with a reason field and no output. The request succeeded — a safety or routing rule decided not to forward it.
  • Tool calls pending return 200 with status: "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

StatusWhen it occurs
400 Bad RequestA required parameter is missing or the request body is malformed.
401 UnauthorizedToken is missing, invalid, expired, or the API key has been revoked or paused.
402 Payment RequiredYour project’s monthly budget cap has been reached.
404 Not FoundThe resource does not exist or belongs to a different account.
422 Unprocessable EntityThe request is well-formed but failed validation (e.g. a required field is blank).
429 Too Many RequestsRate limit exceeded, or a per-key hourly spend cap has been hit.
500 Internal Server ErrorUnexpected server error — safe to retry with exponential back-off.

Auth Errors (401)

error valueCause 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 valuecodeCause and fix
"Monthly budget exceeded."BUDGET_EXCEEDEDYour project’s monthly spend cap was reached. Increase the budget in Settings or wait for the next billing period.

Rate & Spend Errors (429)

error valuecodeCause 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_OPENKey’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_DETECTEDMore than 50 requests in 10 seconds — Olyx detected a likely runaway loop and paused the key.

Validation Errors (422)

error valueCause
"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:

reasonCause
"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

StatusWhat to do
401Check or rotate your API key. Re-authenticate if using a dashboard session.
402Your project’s monthly budget cap was hit. Raise the limit in Settings or wait for the next period.
404Verify the resource ID and that you’re using the correct API key for the project.
422Fix 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.
500Retry 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.

ClassTrigger
Olyx::AuthError401
Olyx::NotFoundError404
Olyx::ValidationError422
Olyx::RateLimitError429 — check .code: "CIRCUIT_OPEN" (spend cap) or "LOOP_DETECTED" (runaway loop)
Olyx::ServerError5xx from the gateway
Olyx::GatewayErrorNetwork timeout, connection refused, or 5xx — internal signal that triggers the fail-open/closed decision
Olyx::CircuitBreakerErrorGateway unreachable and fail_open is false. Request blocked — no ungoverned path to the LLM provider
Olyx::ConfigurationErrorInvalid 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.

ClassTrigger
olyx.AuthError401
olyx.NotFoundError404
olyx.ValidationError422
olyx.RateLimitError429 — check .code: "CIRCUIT_OPEN" (spend cap) or "LOOP_DETECTED" (runaway loop)
olyx.ServerError5xx from the gateway
olyx.GatewayErrorNetwork timeout, connection refused, or 5xx — internal signal that triggers the fail-open/closed decision
olyx.CircuitBreakerErrorGateway unreachable and fail_open is False. Request blocked — no ungoverned path to the LLM provider
olyx.ConfigurationErrorInvalid 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.

ClassTrigger
AuthError401
NotFoundError404
ValidationError422
RateLimitError429 — check .code: "CIRCUIT_OPEN" or "LOOP_DETECTED"
ServerError5xx from the gateway
GatewayErrorNetwork timeout, connection refused, or gateway transport failure
CircuitBreakerErrorGateway unreachable and failOpen is false
ConfigurationErrorInvalid 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.

Was this page helpful?