Error responses¶
All errors are JSON with Content-Type: application/json.
Shape¶
Status code matrix¶
| Status | When | Body |
|---|---|---|
| 400 | .. in /v1/<subpath> |
Invalid path |
| 400 | Validation failure on /api/keys (name / is_active) |
name is required / name must be a string / name must not be empty / name must be at most 120 characters / is_active must be a boolean / key must be a non-empty string / No fields to update |
| 400 | Malformed /v1/chat/completions or /v1/messages payload (non-int max_tokens, etc.) |
per-field validation message |
| 400 | /api/gpu-stats POST missing gpus array |
Missing gpus array |
| 401 | /v1/* without a valid API key |
Unauthorized |
| 401 | /api/* (most) without a session |
Authentication required |
| 401 | /api/login with bad credentials |
Invalid credentials |
| 401 | /api/me while anonymous |
{"authenticated": false} |
| 401 | /api/gpu-stats POST with bad / missing API key |
Unauthorized |
| 403 | Mutating /api/* without (or with wrong) CSRF |
CSRF token missing or invalid |
| 404 | /api/keys/<id> GET / PATCH / DELETE for unknown id |
Key not found |
| 404 | Anything else under non-/v1/ on proxy.worv.ai |
Not found. Use /v1/ endpoints. (served by nginx, not Flask) |
| 409 | POST /api/keys with an explicit key that already exists |
Key already exists |
| 413 | Request body > 10 MiB | (Flask default page) |
| 429 | /api/login exceeded 10/min/IP |
(Flask-Limiter default page) |
| 502 | Upstream returned non-200 on /v1/models discovery |
Backend unavailable |
| 502 | Upstream RequestException not covered by 503/504 |
Backend request failed |
| 503 | Backend connection refused after retry | Backend unavailable |
| 504 | Upstream read timed out | Backend timeout |
Retries¶
The proxy retries upstream connect failures once before failing the request. Read timeouts are not retried — they are surfaced immediately as 504.
Clients should not blindly retry 5xx responses on streaming endpoints — long prefill is normal for big-context Claude Code requests on DeepSeek-V4-Pro (300 s+ to first byte is plausible).
Edge / nginx errors¶
A handful of error paths are served by nginx before Flask sees them:
| Status | Cause |
|---|---|
| 301 | HTTP → HTTPS redirect (every plaintext request) |
| 404 | Path outside /v1/ on proxy.worv.ai ({"error":"Not found. Use /v1/ endpoints."}) |
| 502 | Flask process down (systemd-managed; auto-restart on failure) |
| 504 | nginx proxy_read_timeout exceeded (1800 s; should not happen unless Flask itself hangs) |