Ошибка приходит кодом HTTP и телом в схеме OpenAI. Разбирать текст сообщения не нужно — для ветвления есть code.
{
"error": {
"code": "insufficient_credits",
"message": "Недостаточно средств",
"type": "billing",
"request_id": "if-01J8…"
}
}request_id приходит и в теле, и в заголовке X-Request-Id. Логируйте его: с ним разбирательство занимает минуты, без него — дни.
| Код | Причина | Повторять? |
|---|---|---|
| 400 | Тело не разобрано, превышен контекст модели или площадка не умеет запрошенную возможность | Нет — запрос нужно менять |
| 401 | Ключ отозван, не найден или передан не в том заголовке | Нет |
| 402 | Исчерпан баланс аккаунта или потолок расхода ключа | После пополнения |
| 404 | Модели нет в каталоге или маршрут выключен | Нет |
| 429 | Превышен RPM или RPD ключа | Да, через Retry-After |
| 500 | Ошибка на нашей стороне | Да, с отступом |
| 502 | Площадка не ответила, резервный маршрут тоже | Да, с отступом |
| 503 | Маршрут временно закрыт: все площадки модели деградировали | Да, с большим отступом |
На 429 берите паузу из заголовка Retry-After — она уже рассчитана. На 5xx — экспоненциальный отступ со случайной добавкой: 1, 2, 4 секунды плюс случайные полсекунды. Без случайной добавки все ваши процессы вернутся одновременно и уронят площадку второй раз.
for attempt in range(4):
try:
return client.chat.completions.create(**kwargs)
except APIStatusError as e:
if e.status_code in (400, 401, 402, 404):
raise # повтор не поможет
if e.status_code == 429:
time.sleep(float(e.response.headers.get("Retry-After", 2)))
else:
time.sleep(2 ** attempt + random.random())
raise RuntimeError("не удалось после четырёх попыток")502 означает, что запрос уже уходил как минимум двум площадкам и обе не ответили. Ваш повтор — третья попытка, и торопиться с ней не стоит. Переключатель резервных маршрутов — в настройках ключей.Ответ 200 с finish_reason: "length" означает, что генерация упёрлась в max_tokens и текст оборван на полуслове. Формально всё хорошо, фактически ответ неполон — если разбираете его программой, проверяйте это поле.