Ошибки API и повторы: как не потерять результат и не оплатить дубль
Разбираем 401, 402, 429, 202, таймаут и сетевой обрыв в LLM API: какие ошибки исправлять, когда ждать и как сохранять идентичность платного запроса.

Повторять LLM-запрос можно только после понимания его исхода и с учётом контракта сервиса. Исправьте постоянные ошибки, ограничьте ожидание при временных отказах, а после сетевого обрыва восстанавливайте исходный запрос с тем же телом и идентификатором.
Один код ошибки не описывает все случаи
401 обычно говорит о проблеме с ключом, 403: о праве, 402: о финансовом ограничении. Неверный ID модели или параметр требует исправить запрос. Повтор с тем же неверным телом только добавит шум. При 429 читайте конкретный код и Retry-After, если они есть: ограничение частоты, бюджета и перегрузка могут требовать разных действий. Официальные руководства OpenAI отдельно описывают ошибки и лимиты.
В Kvantora сверяйте собственную страницу ошибок и состояние запроса. HTTP 202 обозначает, что результат ещё обрабатывается; его нельзя превращать ни в окончательный успех с пустым ответом, ни в повод немедленно отправить новую работу. Разведите в коде «исправить», «подождать», «восстановить» и «завершено».
Здесь разбираем, как спроектировать поведение приложения и выбрать проверки. Для воспроизводимого примера в Kvantora ниже есть отдельная короткая практическая инструкция в связанных материалах.
Таймаут не доказывает отказ
Клиент мог отправить всё тело, после чего потерять соединение до ответа. Сервер уже мог принять и выполнить платную операцию. Если приложение создаст новый UUID и повторит запрос, оно рискует оплатить вторую генерацию. Эта ситуация отличается от явного отказа до исполнения. В журнале назовите её «исход неизвестен», пока не получите подтверждение.
Восстановление по прежней идентичности
Для Kvantora сохраняйте исходные endpoint, тело и Idempotency-Key до отправки. Быстрый старт показывает восстановление через повтор send с тем же сохранённым файлом. Новый файл или ID означают новый логический запрос. После восстановления сверяйте итоговый billing.status и запись истории, а не заключайте о деньгах по тому, видел ли пользователь текст.
Ограничьте число попыток и время ожидания
Временные отказы могут пройти после паузы. Если сервер прислал Retry-After, выдержите хотя бы указанное время. Без него используйте возрастающую задержку с небольшим случайным сдвигом и конечным числом попыток. Не складывайте собственный retry-цикл с автоматическими повторами SDK, пока не знаете их суммарное поведение. Документация OpenAI прямо предупреждает о таких вложенных циклах.
Для платного маршрута правило строже: сначала выясните, принят ли исходный запрос и поддерживается ли идемпотентное восстановление. Универсальная библиотека повторов не знает ваш финансовый контракт. Если ей отдаётся POST без сохранённой идентичности, задержка лишь делает дубли менее заметными, а не исключает их.
Состояния нужны и пользователю, и бухгалтерии
Храните для операции внутренний ID, идентификатор запроса к API, время отправки, статус и подтверждённый расход. Возможные состояния: подготовлен, принят, ожидает, завершён, отклонён до работы и неизвестный исход. Набор может отличаться по контракту сервиса, но разница между «не знаю» и «бесплатно отклонён» должна сохраняться.
Интерфейс должен честно показывать «проверяем результат», если исход неизвестен. Кнопка «попробовать снова» может возобновлять исходную операцию, когда это предусмотрено API, а не создавать новую. Для поддержки сохраняйте безопасный trace ID HTTP-обращения отдельно от ID логического запроса. Не просите пользователя присылать секрет ради расследования.
Поддержке нужен путь к ответу, а не сырой стек
Когда пользователь сообщает «деньги ушли, ответа нет», оператору нужны ID логического запроса, его состояние и подтверждённый расчёт. Стек исключения Node.js или Python редко отвечает на этот вопрос. Сделайте внутренний просмотр операции с безопасными техническими данными и ссылкой на историю. Если исход всё ещё неясен, сообщите это прямо, а не обещайте возврат на основании таймаута.
При расследовании не заставляйте пользователя повторять ввод или присылать API-ключ. Запрос может содержать конфиденциальный документ; его копия должна храниться и открываться только по нужным правам. Хорошая система восстановления защищает одновременно деньги, данные и доверие пользователя. Повтор после ошибки: лишь один маленький участок этой системы.
Проверка на синтетическом сервере
Сымитируйте обрыв до приёма тела, после приёма, после первого байта ответа и после текста до итогового расчёта. Для каждого случая запишите ожидаемое поведение клиента и финансовое состояние. Затем проверьте 401, 402, временное ограничение и 202. Такой тест обнаружит скрытые повторы без реальных платных вызовов.
Особенно полезен сценарий перезапуска процесса: запрос уже отправлен, а сервер приложения падает до записи ответа. После запуска он должен найти сохранённую исходную идентичность и восстановить результат. Если UUID жил только в памяти, это невозможно. Исправлять такой дефект в продакшене дорого, поэтому проверьте его до выпуска.
Отдельно ограничьте параллельные попытки восстановления. Два воркера, увидевшие один неизвестный исход, не должны независимо создавать новые операции. Для локального теста запустите их одновременно на одной сохранённой записи и проверьте, что они обращаются к одной идентичности. Это проверяет не красоту задержки между retry, а реальный механизм защиты от дубля.
Простое правило для команды
Новая задача пользователя получает новый ID. Восстановление той же задачи использует прежний ID и неизменное тело. Постоянная ошибка исправляется человеком или кодом до нового запроса. Временная ошибка получает ограниченное ожидание. Неизвестный исход не превращается в fallback к другой модели без проверки, потому что первая могла уже выполнить работу.
Документируйте это правило рядом с клиентом API и тестами. Когда команда знает, что означает каждый статус, сообщения пользователю становятся спокойнее, а учёт расходов точнее. Надёжность здесь не в большом числе повторов, а в способности отличить отказ от потерянного ответа.






