В разработке вызов работает. Теперь вы пишете ту часть, которая будет работать в продакшене, — блок catch. HTTP-ошибка от стороннего API сама по себе ничего не объясняет: красный код статуса и никакого ясного ответа на единственный важный вопрос в 3 часа ночи — проблема в моём запросе, моём ключе, моём тарифе или у них на серверах? И что из этого нужно повторить, а что показать пользователю?
Lingo.dev решает эту проблему структурой. Любая ошибка — с любого эндпоинта, синхронного или асинхронного — возвращается в виде одного и того же JSON-объекта с кодом статуса из фиксированной таблицы. Код статуса — это не просто метка, а инструкция: он подсказывает, нужно ли исправить запрос, сменить ключ, пополнить аккаунт, сбавить темп или повторить попытку. Смотрите на код — и сразу понимаете, что делать дальше. Один обработчик ошибок, завязанный на код статуса, покрывает весь API.
На этой странице
- Формат ошибки
- Коды статуса
- Какие ошибки повторять
- 402 и 429: два разных лимита
- Где искать ошибки асинхронных задач
Формат ошибки#
У каждого ответа вне диапазона 2xx одно и то же тело: JSON-объект с единственным полем message, которое описывает, что пошло не так.
{
"message": "Invalid API key"
}Это и есть весь контракт. Не нужно распаковывать никакую обёртку или отдельно обрабатывать формат ошибок для разных эндпоинтов. И 400 от /process/localize, и 404 при поиске задачи возвращают одну и ту же структуру — различаются только код статуса и текст в message.
Ориентируйтесь на код статуса, а не на текст сообщения
HTTP-код статуса — это стабильный сигнал: стройте обработку ошибок вокруг него. Строка message написана для человека, который читает лог; воспринимайте её как описание, а не как машиночитаемый код ошибки, и не завязывайтесь на точную формулировку.
Коды статуса#
Семь кодов статуса покрывают все ответы. Здесь они сгруппированы по тому, кто должен их устранить, — потому что эта группировка одновременно задаёт и политику повторных попыток.
Вы отправили то, с чем запрос не может сработать (исправьте запрос, не повторяйте вслепую):
| Статус | Что означает |
|---|---|
400 Bad Request | Проверка запроса не пройдена: отсутствует обязательное поле, указана недопустимая локаль, передан HTTP (а не HTTPS) callbackUrl или некорректный payload. |
401 Unauthorized | Заголовок X-API-Key отсутствует или недействителен. См. Аутентификация. |
403 Forbidden | Ключ действителен, но не имеет доступа к запрошенному ресурсу. |
404 Not Found | Ресурс — движок, задача или группа задач — не существует. |
Ваша организация упёрлась в лимит аккаунта (решается через биллинг):
| Статус | Что означает |
|---|---|
402 Payment Required | Организация достигла лимита кредита. |
429 Too Many Requests | Организация достигла дневной квоты токенов. Чтобы увеличить лимит, перейдите на более высокий тариф. |
Что-то сломалось на нашей стороне (временная ошибка — повторите попытку):
| Статус | Что означает |
|---|---|
500 Internal Server Error | Непредвиденный сбой — например, ошибка базы данных или неудачный вызов перевода во всех настроенных моделях в движке. |
401 и 403 выглядят похоже, но означают разное: 401 значит, что мы вообще не смогли определить, кто вызывает API, а 403 — что ключ распознан, но доступ ему запрещён. В случае 401 проблема в самом ключе (смените его или проверьте); в случае 403 — в правах доступа этого ключа.
Какие ошибки повторять#
Первый вопрос, который задаёт любой осторожный интегратор, глядя на таблицу ошибок, — тот самый, на который она обычно не отвечает: что из этого вообще стоит повторять? Ответ — в группировке выше.
- 4xx — не повторяйте вслепую. Ошибки
400,401,403или404описывают состояние вашего запроса. Если отправить тот же запрос ещё раз, вы получите ту же ошибку. Исправьте входные данные, ключ или идентификатор ресурса, а потом отправьте запрос заново. - 402 и 429 — сбавьте темп, затем устраните лимит. Это не временные ошибки на уровне запроса: следующий запрос упрётся в то же ограничение, пока не изменится сам лимит. Не крутите повторы в плотном цикле, покажите ограничение и устраните его (пополните счёт или перейдите на более высокий тариф).
- 500 — повторяйте с backoff. Это единственный класс ошибок, который действительно бывает временным.
500может означать, что на этом вызове таймаут случился у всех настроенных моделей; повторная попытка может попасть на исправную модель. Используйте экспоненциальный backoff и ограничение на число повторов.
Асинхронный API сообщает о результатах иначе
Эта политика повторных попыток относится к синхронным вызовам, которые вы делаете сами. Асинхронный API локализации не возвращает код статуса, который описывает итог выполнения: запрос POST возвращает 202, как только он принят, а каждая целевая локаль запускается как отдельная задача через надёжные фоновые рабочие процессы. Вместо того чтобы ловить код статуса в исходном вызове, вы опрашиваете задачу или получаете результат через webhook. См. где искать ошибки асинхронных задач.
402 и 429: два разных лимита#
Эти два кода уровня аккаунта легко перепутать — оба звучат как «у вас всё закончилось» — и из-за этого разработчик приходит не к тому решению. Это разные лимиты и устраняются они по-разному:
402 Payment Required— организация достигла лимита кредита. Это граница биллинга. Следующий вызов будет и дальше завершаться ошибкой, пока не изменится биллинговый статус вашей организации.429 Too Many Requests— организация достигла дневной квоты токенов. Это лимит использования, который со временем сбрасывается, а повысить его можно переходом на более высокий тариф.
Почему важно различать их в обработчике: 402 требует действия по биллингу со стороны человека, а 429 — это квота, которую либо нужно дождаться, пока она сбросится, либо увеличить сменой тарифа. Если свести оба случая к общему сообщению вроде «проблема с оплатой», вы скроете, какой именно рычаг оператору нужно задействовать.
Тело ответа 402 выглядит так же, как и у любой другой ошибки, — только код статуса показывает, что речь о лимите кредита:
{
"message": "Organization has reached its credit limit"
}Где искать ошибки асинхронных задач#
Здесь важно провести чёткую границу: именно в этом месте обработчик по коду статуса перестаёт быть подходящим инструментом.
Коды статуса на этой странице относятся к транспортному уровню: они описывают, принял ли API ваш HTTP-запрос и смог ли его обработать. 202 от асинхронного API означает, что запрос принят, — но не то, что перевод завершился успешно. Асинхронная задача может быть принята без проблем и всё равно упасть позже, например если у модели истечёт таймаут прямо во время выполнения. Такая ошибка не приходит как HTTP-код статуса в исходном вызове; она фиксируется в самой задаче.
Поэтому сбои в асинхронном режиме проявляются в трёх местах — и ни одно из них не входит в эту таблицу:
- Статус отдельной задачи. У локали, завершившейся с ошибкой, в задаче будут
status: "failed"иerrorMessage. См. статусы задач. - Статус группы. Когда часть локалей завершается успешно, а часть — с ошибкой, группа получает статус
partial— успешно завершённые локали всё равно доставляются. См. отслеживание группы задач. - Доставка webhook. Ошибка приходит как событие
translation.failedс полемerror. См. доставка webhook.
Есть ещё одно различие, на котором часто спотыкаются: сбой на некритичном этапе pipeline не приводит к падению задачи. Задача завершается со статусом completed_with_warnings и предупреждениями по отдельным шагам, а не ошибкой. Это вопрос наблюдаемости pipeline, а не кодов ошибок — см. наблюдение за запусками pipeline.
Следующие шаги#
Хороший обработчик ошибок начинается с двух кодов, которые вы почти наверняка увидите первыми при интеграции: аутентификация и точки, где асинхронная обработка сообщает о собственном результате.
