Skip to content

Status codes

The gateway uses standard HTTP status codes. This page is the single source of truth for "what does this code mean here".

Status When
200 OK Successful completion (non-streaming) or successful stream start (streaming).
400 Bad Request Malformed JSON body; multimodal content rejected by the policy layer.
401 Unauthorized Missing Authorization header, header malformed, or bearer token ≤ 10 characters.
403 Forbidden Privacy gate (A force-free request that violates privacy policy must fail closed.), Scout on sensitive (Scout cannot be called on sensitive data.), DENY action, or STOP_FOR_HUMAN for non-ROUTE/SCOUT decisions.
404 Not Found Unknown route (anything other than /healthz, /metrics, /v1/chat/completions, /v1/route/decision).
422 Unprocessable Entity Pydantic validation error. The detail is a list of field-level violations.
500 Internal Server Error OpenRouter returned a non-retryable 4xx, or all retries exhausted. May include a fallback chain that already failed.
502 Bad Gateway OpenRouter transport error after retries.
503 Service Unavailable Same as 502.
504 Gateway Timeout Same as 502.

How to distinguish 403 sub-cases

The detail field is the key:

Detail Cause
A force-free request that violates privacy policy must fail closed. Privacy gate; client used smart-router-free or another free override on sensitive content.
Scout cannot be called on sensitive data. X-Scout: true on sensitive.
Request denied by policy: <cause> A DENY action. The cause is in the detail.
Anything else STOP_FOR_HUMAN. Common causes: all models unhealthy, hard budget cap exceeded, privacy hard gate on free-tier task, SAFE_DEGRADED + Pro request.

How to distinguish 500 sub-cases

Detail Cause
OpenRouter API error: <code> Non-retryable upstream 4xx. Common: 400 (bad request shape), 401 (server-side OpenRouter key bad), 403 (provider refusal).
OpenRouter API failed after retries with status <code> Retryable 5xx/429 after 3 attempts.
OpenRouter connection failed: <reason> Transport error after retries.

For Pro requests, the gateway attempts one fallback to GLM-5.2 before returning 500. So if you see 500 on a Pro call, both Pro and GLM-5.2 failed.

What is never returned

  • 200 with a synthetic success body when the upstream actually failed.
  • 200 with an empty choices array on a hard-cap denial.
  • 200 with an error detail inside the response body.

If you see any of these, file a bug.

See also