Обработка ошибок LLM API: retry, 429, 503 и запасная модель
Не каждую ошибку стоит повторять, и не каждую можно пережить на той же модели. Разбираем коды Zukko, паузы между попытками и цепочку резервных моделей с кодом.
5 мин · · ошибки · разработка
Бот или пайплайн, который падает на первом же 503, приходится перезапускать руками. А скрипт, который бесконечно повторяет запрос с неверным ключом, просто греет сервер. Грамотная обработка ошибок LLM API — это три решения: какие коды повторять, сколько ждать между попытками и когда переключаться на другую модель. Ниже — таблица кодов Zukko и готовый код на Python.
Коды ошибок и что с ними делать
Zukko отвечает на ошибки обычным HTTP-статусом и JSON-телом в формате OpenAI; уточняющий код лежит в поле error.code. Разбирайте ошибку по статусу и коду, а не по тексту сообщения.
| Статус | Что значит | Повторять? |
|---|---|---|
| 400 | Некорректный запрос: параметры, формат сообщений | Нет, исправьте запрос |
| 401 | Неверный или отозванный ключ | Нет |
| 402 | Не хватает баланса | Нет, сначала пополните баланс |
403 model_not_allowed_for_key | Модели нет в списке разрешённых для ключа | Нет, поправьте allowed_models или возьмите разрешённую модель |
404 model_not_found | Неизвестное имя модели | Нет, проверьте id по GET /v1/models |
| 413 | Слишком большой запрос | Нет, уменьшите запрос |
| 429 | Исчерпан лимит ключа или ограничение у провайдера модели (общего rate limit у Zukko нет) | Да, с паузой — кроме лимитов ключа (см. ниже) |
| 503 | Провайдер модели недоступен | Да, с паузой или на другой модели |
503 model_disabled | Модель выключена | Нет, возьмите другую модель из GET /v1/models |
Плюс сетевые сбои и таймауты: соединение оборвалось, ответа нет — такой запрос тоже имеет смысл повторить.
Особый случай: 429 из-за лимита ключа
У каждого ключа в кабинете можно задать дневной и месячный потолок расхода. Когда он исчерпан, приходит 429 с кодом key_daily_limit_reached или key_monthly_limit_reached. Повторять такой запрос через пару секунд бессмысленно: лимит сбросится только в начале следующих суток или месяца по UTC, либо когда вы поднимете его в кабинете. Подробнее — в разделе о лимитах ключа.
Обработка ошибок LLM API: правила повторов
- Экспоненциальная пауза со случайной добавкой. 1, 2, 4 секунды плюс случайные доли секунды — чтобы сотня воркеров не ударила в сервер одновременно.
- Заголовок
Retry-After. Если сервер прислал его в ответе, ждите столько, сколько сказано. - Ограниченное число попыток. Две-три попытки на одной модели, потом — переключение на резерв или ошибка наверх.
- Защита от двойной оплаты. Если запрос упал по таймауту, он мог дойти до модели. Zukko принимает заголовок
Idempotency-Key: повтор с тем же значением вернёт сохранённый успешный ответ и не спишет деньги второй раз. Генерируйте одно значение на логический запрос и передавайте его во всех повторах. Два одновременных запроса с одним ключом идемпотентности оба уйдут к модели, поэтому повторяйте только после того, как предыдущая попытка завершилась. - Отключите встроенные повторы SDK, если пишете свои. OpenAI SDK по умолчанию сам повторяет 408, 409, 429 и 5xx два раза. Вместе с вашим циклом получится до девяти запросов вместо трёх.
Как встроить такие повторы в партию из тысяч запросов — в статье об асинхронной обработке на Python.
Резервная модель
Ошибка 503 обычно означает проблему у конкретного провайдера, поэтому резерв разумно брать у другого вендора. Пример цепочки от сильной модели к дешёвой (цены на 6 октября 2026, $ за 1M токенов, индекс силы — Intelligence Index от Artificial Analysis из каталога Zukko):
| Модель | Вход / выход | Индекс силы | Картинки на вход |
|---|---|---|---|
claude-opus-5-5 | 0.4 / 2 | 58 | да |
gpt-6.1-sol | 0.27 / 1.32 | 52 | да |
gemini-3.8-flash | 0.025 / 0.088 | 41 | нет |
Три практических замечания:
- Резерв должен уметь то же, что основная модель. Если в запросах есть картинки,
gemini-3.8-flashне подойдёт — возьмите, например,gpt-6-sol. - Добавьте резервные модели в
allowed_modelsключа. Иначе вместо ответа резерва вы получите 403. - Проверьте резерв заранее. Прогоните на нём 20–30 типичных запросов: держит ли он формат ответа, который ждёт ваш код. Узнавать это во время сбоя — поздно.
Актуальный список и цены — GET https://api.zukko.pro/v1/models или страница «Модели» в кабинете. Сам список отдаётся без ключа и бесплатно, см. справочник по /v1/models.
Код на Python
import os
import random
import time
import uuid
import openai
from openai import OpenAI
client = OpenAI(
base_url="https://api.zukko.pro/v1",
api_key=os.environ["ZUKKO_API_KEY"],
max_retries=0, # повторами управляем сами
timeout=120,
)Что здесь происходит:
call_modelповторяет запрос на одной модели только при 429 (кроме лимитов ключа), 5xx и сетевых сбоях. Всё остальное сразу превращается вFatalAPIError: 401, 402 и 403 на другой модели не исправятся.- 503
model_disabledуходит наверх без повторов, иcompleteсверяет оставшуюся цепочку со свежим спискомGET /v1/models. Если из цепочки ничего не осталось, берётся первая доступная модель из запасного спискаEMERGENCY. - У исключений OpenAI SDK поле
codeберётся изerror.codeв теле ответа, поэтому строковые сравнения с текстом сообщения не нужны. - Функция возвращает имя модели, которая ответила, — логируйте его, чтобы видеть, как часто срабатывает резерв.
Стриминг и агенты
В потоковом режиме статус 200 приходит до того, как модель начала отвечать, и ошибка может прийти уже внутри потока. Повтор нужно строить вокруг всего цикла чтения, а не только вокруг вызова create. Если пользователь уже видел половину ответа, переотправка на другую модель покажет ему другой текст — в чате честнее сообщить о сбое и предложить повторить.
В агентах с длинными сессиями смена модели посреди задачи меняет и стоимость, и поведение. Для Claude учитывайте ещё и кэш: при переключении на другую модель накопленный кэш промптов не переносится, и следующий запрос пойдёт по цене свежего входа.
Отдельный ключ под каждый сервис с дневным лимитом в кабинете на странице API-ключи ограничивает ущерб, если цикл повторов всё-таки пойдёт вразнос.
Частые вопросы
Почему бы не повторять все ошибки подряд?
400, 401, 402 и 403 вызваны самим запросом, ключом или балансом. Повтор вернёт ту же ошибку, а в логах появится шум, за которым не видно настоящей причины.
Как отличить свой лимит ключа от 429 провайдера?
По полю error.code: key_daily_limit_reached или key_monthly_limit_reached означают, что исчерпан потолок, который вы сами задали ключу. Своего общего ограничения частоты у Zukko нет, поэтому любой другой 429 пришёл от провайдера модели. Подробнее о 429 — в статье про ошибку 429.
Что делать при 503 model_disabled?
Не повторять: модель выключена. Возьмите другую из GET https://api.zukko.pro/v1/models — код выше делает это автоматически.
Сколько попыток ставить?
Две-три на одну модель с паузой 1–4 секунды и не больше трёх моделей в цепочке. Если и это не помогло, лучше вернуть понятную ошибку, чем держать пользователя минуту в ожидании.
zukko
Платите в 25×
меньше
за Claude Code & ChatGPT
Один ключ fetch_* и base URL api.zukko.pro — в Cursor, Claude Code, Codex и любом SDK.
Похожие статьи
- Как обновить Claude Code, откатить версию или полностью удалитьКоманды для обновления, отката и удаления Claude Code по способу установки, и как не потерять настройки подключения к шлюзу при переустановке.
- Ключи OpenAI API в открытом доступе: почему ими нельзя пользоватьсяОткуда берутся «бесплатные» ключи на GitHub, почему пользоваться ими опасно и для вас, и для владельца, и как защитить собственный ключ от такой же утечки.
- Ошибки GLM API: почему не работает запрос и как это исправитьНеверный id модели, переполненный контекст 128K, ключ, баланс, лимиты: типичные причины ошибок GLM API и быстрый способ найти свою.
- This organization has been disabled в Claude API: что это и как продолжитьКлюч Anthropic внезапно отвечает «organization has been disabled». Объясняем, что это за ошибка, что проверить и как перенести SDK и Claude Code на другой провайдер.