Errors
Sakrylle API surfaces errors at two layers: the gateway layer (auth, billing, routing) and the upstream layer (the actual OpenAI / Anthropic service). Both follow standard HTTP semantics, but their response bodies differ slightly. This page helps you tell them apart and locate the source.
HTTP status codes
| Status | Meaning | Source | What to do |
|---|---|---|---|
400 Bad Request | Malformed body, missing fields, misspelled model | Gateway / Upstream | Validate your JSON; cross-check required fields against the API reference |
401 Unauthorized | Missing Authorization header or revoked key | Gateway | See common authentication errors |
402 / insufficient balance | Account balance or plan quota exhausted | Gateway | Top up or upgrade in the Console |
403 Forbidden | Key is valid, but the group does not have access to that model | Gateway | Check the model's group in Models and pricing |
404 Not Found | Model name or route does not exist | Gateway / Upstream | See the "Models that do not exist" list in Models and pricing |
429 Too Many Requests | Gateway or upstream rate limit | Gateway / Upstream | Exponential backoff — see retry strategy below |
500 Internal Server Error | Internal gateway or upstream error | Gateway / Upstream | Retry briefly; if persistent, contact support |
502 Bad Gateway | Upstream connection failed or returned an invalid response | Gateway | Usually an upstream blip — retry |
503 Service Unavailable | Upstream rejected the request (overloaded / maintenance) | Upstream | Retry; watch status.sakrylle.com |
504 Gateway Timeout | Upstream timed out | Gateway | Check whether the prompt is too large; reduce max_tokens or split the request |
Gateway errors
In addition to standard HTTP status codes, the gateway returns fine-grained business error codes in the response body to help pinpoint issues:
| Error code | HTTP status | Meaning | What to do |
|---|---|---|---|
GROUP_DELETED | 403 | The API key's bound group has been deleted | Recreate the key in the Console and bind it to a valid group |
GROUP_DISABLED | 403 | The API key's bound group has been disabled | Ask an administrator to enable the group, or switch to another group's key |
GROUP_NOT_ALLOWED | 403 | The requested group does not allow this operation | Check that the OAuth token's authorized group is correct |
GROUP_OVERRIDE_UNSUPPORTED | 409 | The current group does not support model-name-prefix group override | Verify compatibility between the OAuth token and the target group |
GROUP_UNAVAILABLE | 400 | The specified group is unavailable | Check that the group ID is correct, or contact an administrator to confirm group status |
SUBSCRIPTION_NOT_FOUND | 403 | Subscription does not exist or has expired | Renew in the Console or switch to wallet balance mode |
SUBSCRIPTION_INVALID | 403 | Subscription is invalid (plan type does not match the request) | Check whether the current plan supports the requested model or endpoint |
USAGE_LIMIT_EXCEEDED | 429 | Usage exceeds limits (daily/weekly/monthly caps or total quota) | Wait for the limit to reset, or upgrade the plan / top up |
INSUFFICIENT_BALANCE | 403/402 | Account balance is insufficient | Top up in the Console |
api_key_in_query_deprecated | 400 | API key passed via query parameter (deprecated) | Switch to the Authorization: Bearer <key> header |
Error formats
The Sakrylle gateway passes through upstream native error formats. Different platforms have different error response structures:
Anthropic native format
Errors returned by /v1/messages and /v1/messages/count_tokens:
{
"type": "error",
"error": {
"type": "permission_error",
"message": "Request not allowed"
}
}The top-level type: "error" is the Anthropic-style marker. Possible error.type values include invalid_request_error, authentication_error, permission_error, rate_limit_error, api_error, etc.
Google native format
Errors returned by some Google platform models:
{
"error": {
"code": 403,
"message": "Permission denied",
"status": "permission_denied"
}
}The error.status field uses standard status enumerations from the Google API Design Guide, such as permission_denied, resource_exhausted, invalid_argument, etc.
Response body shapes
OpenAI compatible
Used by /v1/chat/completions, /v1/responses, /v1/images/*.
{
"error": {
"message": "Invalid model: gpt-foo",
"type": "invalid_request_error",
"code": "model_not_found"
}
}Fields:
message— human-readable descriptiontype— broad category:invalid_request_error,authentication_error,rate_limit_error,api_error, etc.code— finer-grained code (field naming varies by upstream — branch on the HTTP status code first)
Anthropic native
Used by /v1/messages, /v1/messages/count_tokens.
{
"type": "error",
"error": {
"type": "invalid_request_error",
"message": "max_tokens: Field required"
}
}The top-level type: "error" is the Anthropic-style marker that distinguishes an error from a normal response.
Error sources
| Category | Source | Typical message | Retry helps? |
|---|---|---|---|
| Gateway error | Sakrylle gateway | unauthorized, insufficient quota, model not configured | No — fix the account or configuration first |
| Upstream error (passthrough) | OpenAI / Anthropic | rate_limit_exceeded, overloaded_error, internal_server_error | Yes — exponential backoff |
Quick rules of thumb:
- 4xx + message about "key", "quota", "plan", "group" → gateway error
- 5xx or 429 + message about "overloaded", "upstream", "timeout" → upstream error
Debug checklist
Authorization: Bearer <key>header is intact (theBearerprefix is required)- Model name appears in the response of
GET /v1/models - The account belongs to the group that has access to that model
- Console balance covers the estimated cost of the request
- Request body is valid JSON (use
jq .locally to verify) max_tokensis reasonable, and the response is not truncated by the upstream context limit- High-concurrency scenarios are not hitting rate limits (look for clusters of
429) - status.sakrylle.com is not reporting an upstream incident
Retry strategy
Retry on 429, 500, 502, 503, 504. Do not retry on 400, 401, 403, 404 — fix the request or account configuration first.
Recommended parameters:
- Exponential backoff:
delay = base * 2^attempt, withbase = 1s - Jitter: add ±20% random jitter to
delayto avoid thundering herd - Caps: at most 5 retries, single wait capped at 30s
- Total budget: keep total time per business call under 60s
Pseudo-code:
import random, time
def retry(call, max_attempts=5):
for attempt in range(max_attempts):
try:
return call()
except RetryableError as e:
if attempt == max_attempts - 1:
raise
delay = min(30, (2 ** attempt)) * (1 + random.uniform(-0.2, 0.2))
time.sleep(delay)If you use the official SDKs, the Python and Node clients of openai and anthropic already implement retries that back off on 429 and 5xx. Usually you only need to set max_retries.
Contact support
Send the following to support@sakrylle.com:
- Account email
- Request time (UTC, minute precision)
- Endpoint (e.g.
/v1/chat/completions) - Model name
- HTTP status code and the full response body (with the API key removed)
We will trace the call against the gateway logs (retained for 30 days).
