Обработка ошибок
Формат ошибки зависит от адреса, на который вы отправили запрос. В каждой ошибке есть ID запроса.
Формат ошибок
На /v1/chat/completions, /v1/responses и остальных адресах ошибка приходит в формате OpenAI:
{
"error": {
"message": "Invalid or missing API key. Request ID: req_0be8…",
"type": "authentication_error",
"code": "authentication_error",
"request_id": "req_0be8…"
}
}На /v1/messages ошибка приходит в формате Anthropic, его понимают SDK Anthropic и Claude Code:
{
"type": "error",
"error": {
"type": "authentication_error",
"message": "Invalid or missing API key. Request ID: req_0be8…"
},
"request_id": "req_0be8…"
}ID запроса есть в поле request_id, в конце текста сообщения и в заголовке X-Request-Id. Присылайте его в поддержку: по нему мы найдём запрос.
Коды ошибок
Значения в колонке code приходят в формате OpenAI. В формате Anthropic ту же причину показывают HTTP-статус и тип ошибки.
| HTTP | code | Что сделать |
|---|---|---|
400 | model_required bad_request invalid_request | Проверьте тело запроса: JSON и обязательные поля. |
400 | model_not_found | Проверьте имя модели в GET /v1/models. |
400 | context_length_exceeded | Сократите запрос или возьмите модель с большим контекстом. |
400 | image_input_not_supported | Модель не принимает картинки, возьмите модель со зрением. |
400 | content_policy_violation | Запрос нарушает правила использования. |
401 | authentication_error | Неверный, отозванный или не переданный ключ. На адресах картинок и видео поле code может прийти как unauthorized, тип остаётся authentication_error. |
402 | insufficient_quota | Пополните баланс. |
402 | account_frozen | Аккаунт приостановлен, напишите в поддержку. |
403 | model_not_in_channel | Модели нет в канале этого ключа: смените канал ключа или модель. |
404 | not_found | Неизвестный адрес API. |
413 | request_too_large | Тело запроса больше 32 МБ. |
429 | rate_limited family_rate_limited | Слишком частые запросы, повторите через несколько секунд. |
429 | org_queue_full | Слишком много одновременных запросов по ключу, дождитесь завершения текущих. |
429 | queue_full queue_timeout | Сервис загружен, повторите через несколько секунд. |
429 | low_balance | Баланс почти пуст, поэтому запросы идут по одному. Пополните баланс. |
500, 502 | server_error empty_response | Сбой при обработке, повторите запрос. |
503 | model_overloaded | Модель перегружена, повторите позже. |
503 | maintenance auth_unavailable service_unavailable | Временная недоступность, повторите через несколько секунд. |
Ошибки выбора канала (channel_not_allowed, fallback_not_allowed, channel_unavailable, invalid_state) разобраны на странице Каналы Premium и Cheap.
Retry-After
У ответов 429 и 503 есть заголовок Retry-After: время ожидания в секундах.
Ошибки в стриме
Если ошибка случилась после начала стрима, код ответа уже 200, поэтому ошибка приходит событием:
- формат OpenAI:
data: {"error": {…}}, затемdata: [DONE]; - формат Anthropic:
event: errorс телом ошибки; - Responses API: событие
response.failed.
Если модель оборвала ответ на середине, придёт ошибка с кодом upstream_stream_interrupted или stream_incomplete. Ответ не закончен, повторите запрос.
Оплата при ошибках
Если ошибка случилась до начала ответа, деньги не списываются. Если ответ оборвался на середине, списываются только токены, которые модель уже выдала.
Стратегия повторов
Рекомендуем такой порядок:
- 429 и 503: повторяйте с задержкой от 1 секунды, удваивая её (1, 2, 4, 8 секунд, не больше 60). Если пришёл Retry-After, ждите столько, сколько он указывает.
- 500 и 502: повторите до 3 раз с паузой в секунду. Если ошибка держится, напишите в поддержку и приложите ID запроса.
- 400, 401, 402, 403, 404, 413: не повторяйте. Исправьте запрос, ключ, баланс или канал.
import time
import requests
def call_api(payload, max_retries=3):
for attempt in range(max_retries):
response = requests.post(
"https://api.claudexia.tech/v1/messages",
headers={
"x-api-key": "sk_cdx_YOUR_KEY",
"anthropic-version": "2023-06-01",
"Content-Type": "application/json",
},
json=payload,
)
if response.status_code == 200:
return response.json()
if response.status_code in (429, 503):
wait = int(response.headers.get("Retry-After", 2 ** attempt))
time.sleep(wait)
continue
if response.status_code in (500, 502):
time.sleep(1)
continue
response.raise_for_status()
raise Exception("Max retries exceeded")