ОСНОВНОЕ

Ошибки API: коды, тексты и что делать

Каждая ошибка возвращается в конверте OpenAI с соответствующим HTTP-статусом - совместимо с обработкой ошибок в OpenAI SDK. Ниже - раздел на каждый код: точный текст, причина, проверки по шагам и ответ на вопрос, имеет ли смысл повтор.

Форма ошибки

Ответ с ошибкой всегда JSON. Если в теле пришёл HTML - это ответил не наш API, а промежуточный фильтр или прокси; такой случай разобран ниже.

JSON
{
  "error": {
    "message": "Insufficient credits.",
    "type": "insufficient_quota",
    "param": null,
    "code": "insufficient_quota"
  }
}

Разбирать ошибку следует по error.code - он точнее статуса: за одним 402 стоят и пустой баланс, и упёршийся в потолок ключ.

Все коды разом

400invalid_request_errorНеверное тело запроса или параметр.
400content_policy_violationКонтент отклонён модерацией.
401authentication_errorКлюч отсутствует или неверен.
402insufficient_quotaНедостаточно кредитов (insufficient_quota) либо исчерпан потолок трат ключа (spend_limit_exceeded).
403permission_errorКлюч настоящий, но поверхность вне его скоупов (insufficient_scope).
404not_found_errorМодель не существует или недоступна.
409invalid_request_errorЗапрос с этим Idempotency-Key ещё выполняется (idempotency_conflict).
429rate_limit_errorПревышен лимит запросов ключа.
500api_errorВнутренняя ошибка шлюза (internal_error).
502api_errorОшибка на стороне модели.
503service_unavailableМодель временно недоступна.
504api_errorЗапрос превысил допустимое время.

400 - invalid_request_error

Тело запроса не прошло проверку до того, как его увидела модель: не хватает обязательного поля, значение вне допустимого диапазона или запрошенный режим недоступен у этой модели. Поле param в ответе называет виновника.

Что вы видите

`messages` is required and must be a non-empty array. (param: "messages")
`model` is required. (param: "model")
`prompt` is required. (param: "prompt")
Failed to process the reference image. (param: "image")
Image editing is not available for `<model>`. (param: "image")
Resolution 4k is not available for `<model>`. (param: "resolution")
Text-to-video is not available for `<model>` - a reference image is required.
Upstream rejected the request.
Content was rejected by the moderation system. (type: content_policy_violation)

Что проверить

  • Прочитайте error.param - он указывает точное поле, а не «где-то в теле».
  • Проверьте, что messages - непустой массив, а model - строка из каталога.
  • Для изображений и видео сверьтесь с режимами модели: редактирование картинки и image-to-video есть не у всех.
  • Если type равен content_policy_violation, дело не в форме запроса: контент отклонила модерация - переформулируйте промпт.

Ретраить или нет

NO RETRYПовтор с тем же телом даст тот же ответ. Ретрай имеет смысл только после исправления запроса.

401 - authentication_error

Ключ не пришёл, не существует или отозван. Ключи хранятся как SHA-256-хеш, поэтому «посмотреть» существующий ключ нельзя - только выпустить новый.

Что вы видите

Missing API key. (code: invalid_api_key)
Invalid API key provided. (code: invalid_api_key)

Что проверить

  • Заголовок должен быть Authorization: Bearer ar-… (принимается и x-api-key).
  • Проверьте, что ключ не обрезан при копировании и не содержит переносов строки.
  • Убедитесь, что ключ не отозван в кабинете: отозванный ключ отвечает тем же 401.
  • Проверьте base URL: https://api.altrouter.ai/v1 - ключ ar-… не примет чужой шлюз.

Ретраить или нет

NO RETRYНичего не изменится до замены ключа или заголовка.

402 - insufficient_quota

Денег не хватает, чтобы зарезервировать стоимость запроса, - либо на балансе организации, либо в рамках потолка трат конкретного ключа. Это разные коды, и лечатся они по-разному.

Что вы видите

Insufficient credits. (code: insufficient_quota)
This key has reached its month spend limit. (code: spend_limit_exceeded)

Что проверить

  • Посмотрите error.code: insufficient_quota - это баланс, spend_limit_exceeded - потолок ключа.
  • При insufficient_quota пополните баланс в кабинете (карта РФ, СБП, крипта).
  • При spend_limit_exceeded баланс может быть полным: поднимите или снимите лимит ключа. Окна - день, неделя, месяц и «за всё время».
  • Учтите резервы: перед запросом удерживается оценочная стоимость, поэтому доступная сумма меньше баланса на размер незакрытых холдов.

Ретраить или нет

NO RETRYТолько после пополнения, изменения лимита ключа или смены окна (день/неделя/месяц).

403 - permission_error

Ключ настоящий, но обращается к поверхности вне своих скоупов. Второй, редкий случай - адрес клиента в блок-листе: такой запрос отбивается до маршрутизации и до аутентификации.

Что вы видите

API key lacks scope: videos (code: insufficient_scope)
Access denied. (code: access_denied)

Что проверить

  • Сверьте скоупы ключа в кабинете с поверхностью запроса: chat, images, videos.
  • Ключ без списка скоупов имеет доступ ко всему; ограниченный ключ - только к перечисленным.
  • Если нужен доступ к другой поверхности, выпустите новый ключ с нужным скоупом, а не правьте запрос.
  • Код access_denied означает блокировку по адресу, а не проблему с ключом.

Ретраить или нет

NO RETRYПовтор не поможет: нужен ключ с нужным скоупом.

404 - not_found_error

Запрошенной модели нет в каталоге либо она относится к другой поверхности - например, видеомодель в /v1/chat/completions. Тот же статус отдаётся на неизвестный путь.

Что вы видите

The model `gpt-5` does not exist or is not a chat model. (code: model_not_found)
The model `x` does not exist or is not an image model. (code: model_not_found)
Unknown endpoint. (code: unknown_endpoint)

Что проверить

  • Возьмите id ровно из GET /v1/models или из каталога - идентификаторы это слаги вида gpt-5.4, gemini-2.5-flash.
  • Проверьте поверхность: чат-модели идут в /v1/chat/completions, изображения - в /v1/images/generations, видео - в /v1/videos.
  • При коде unknown_endpoint проверьте путь и базовый адрес: https://api.altrouter.ai/v1.

Ретраить или нет

NO RETRYПовтор бессмысленен - меняйте id модели или путь.

429 - rate_limit_error: превышен лимит запросов

Ключ вышел за свои запросы в минуту. Лимит по умолчанию - 600 rpm на ключ, у отдельного ключа может стоять свой. Окно фиксированное, длиной 60 секунд, и считается по ключу, а не по организации.

Что вы видите

Rate limit exceeded. (code: rate_limit_exceeded, Retry-After: 60)
Too many sign-in emails requested. Try again later.

Что проверить

  • Прочитайте заголовок Retry-After - в нём число секунд до конца окна.
  • Смотрите x-ratelimit-limit и x-ratelimit-remaining в каждом ответе: остаток виден до того, как лимит кончится.
  • Разнесите пиковую нагрузку по нескольким ключам либо запросите повышение rpm для ключа.
  • Не наращивайте параллелизм в ответ на 429 - это только ускоряет исчерпание окна.

Ретраить или нет

RETRYДа - через Retry-After секунд (обычно 60). 429 от самого провайдера до вас не доходит: маршрутизатор уводит такой запрос на резервный маршрут.

500, 502, 503, 504 - сбои на нашей стороне и у провайдера

500 - внутренняя ошибка шлюза; её текст всегда один и тот же, детали остаются в наших логах и клиенту не показываются. 502 - провайдер ответил ошибкой. 503 - живого маршрута для модели не нашлось: маршрутизатор уже перебрал резервные провайдеры, прежде чем вернуть этот статус. 504 - запрос не уложился в отведённое время.

Что вы видите

Internal server error. (500, code: internal_error)
The upstream provider returned an error. (502, code: upstream_error)
Upstream authentication failed. (502, code: upstream_error)
No healthy provider is available for this model. (503, code: no_provider_available)
All providers failed (last: kie 500). (503, code: no_provider_available)
The request exceeded the allowed time. (504, code: timeout)

Что проверить

  • Повторите с экспоненциальной задержкой - это единственная категория, где повтор действительно меняет исход.
  • Если 503 держится, попробуйте другую модель: статус относится к маршрутам конкретной модели, а не ко всему шлюзу.
  • При 504 сократите ожидаемую длину ответа (max_tokens) или включите стриминг: SSE отдаёт первые токены, не дожидаясь конца генерации.
  • Для /v1/images и /v1/videos повторяйте с заголовком Idempotency-Key, чтобы не создать вторую генерацию и второе списание.

Ретраить или нет

RETRYДа, с экспоненциальной задержкой: 1, 2, 4, 8 секунд и ограничение по числу попыток. Незакрытый холд освобождается автоматически, так что неудачный запрос не съедает баланс.

Какие коды ошибок можно ретраить

Короткий ответ: повторять имеет смысл 429 и пятисотые. Клиентские четырёхсотые описывают состояние вашего запроса, ключа или счёта - оно само не изменится.

400НЕТИсправьте тело запроса - поле названо в error.param.
401НЕТЗамените ключ или заголовок.
402НЕТПополните баланс или поднимите лимит ключа.
403НЕТНужен ключ с подходящим скоупом.
404НЕТПроверьте id модели и путь.
409НЕТДождитесь первого запроса с тем же Idempotency-Key.
429ДАЧерез Retry-After секунд (окно - 60 секунд).
500ДАЭкспоненциальная задержка: 1, 2, 4, 8 секунд.
502ДАТо же: провайдер мог уже восстановиться.
503ДАТо же, но при устойчивом 503 смените модель.
504ДАПовтор плюс более короткий ответ или стриминг.
499НЕТСоединение разорвал сам клиент - сбоя не было.
!
Повторяя запросы к /v1/images и /v1/videos, передавайте заголовок Idempotency-Key: повторный вызов с тем же ключом вернёт результат первого, а параллельная попытка получит 409 idempotency_conflict вместо второй генерации и второго списания.

Forbidden: access denied by security policy - и другие ошибки не от API

Часть сообщений, с которыми приходят в поддержку, наш API не возвращает вовсе. Отличить их просто: в теле ответа HTML-страница, а не JSON, и в ней нет ни error.type, ни error.code.

Forbidden: access denied by security policy.
Sorry, you have been blocked. You are unable to access openrouter.ai

Так отвечает защитный фильтр сайта (WAF, чаще всего Cloudflare), когда не пропускает соединение по адресу клиента или его стране. До API дело не доходит, поэтому ни ключ, ни баланс, ни тело запроса на результат не влияют, а повтор ничего не меняет. Выхода два: другой сетевой маршрут - либо шлюз, который ходит к моделям сам. У altrouter запросы идут на api.altrouter.ai, а к OpenAI, Anthropic и Google ходим мы; VPN не нужен ни для API, ни для кабинета, ни для оплаты.

i
Наш собственный 403 выглядит иначе: это JSON с type: "permission_error" и кодом insufficient_scope - у ключа нет нужного скоупа.

Коды, которые видны только в логах

Статус 499 с кодом client_closed - это не сбой сервиса: так помечается запрос, соединение по которому разорвал сам клиент, например при отмене стрима. Код stream_error означает обрыв уже начавшегося стрима: заголовки со статусом 200 к тому моменту ушли клиенту, поэтому HTTP-кода у такой ошибки нет - она видна в журнале запросов.

Частые вопросы

Что значит «Forbidden: access denied by security policy»?
Это ответ защитного фильтра сайта (WAF), а не API модели: он приходит HTML-страницей, в которой нет ни JSON-конверта, ни поля error.type. Так отвечают, когда запрос пришёл с адреса или из страны, которые фильтр не пропускает, - чаще всего с этим текстом сталкиваются те, кто открывает openrouter.ai или его API из России. Ни ключ, ни повтор запроса тут не помогают: нужен либо другой сетевой маршрут, либо шлюз, который ходит к моделям сам. У altrouter любая ошибка возвращается JSON-ом в конверте OpenAI, а 403 означает только одно: у ключа нет нужного скоупа.
Что делать с «Sorry, you have been blocked. You are unable to access openrouter.ai»?
Это страница блокировки Cloudflare: её отдаёт сам сайт, до API дело не доходит. В ответе HTML с Ray ID вместо JSON, поэтому OpenAI SDK показывает не ошибку модели, а мусор в теле. Ключ, баланс и код запроса ни при чём - соединение отсекли по адресу клиента. Помогает смена сетевого маршрута или шлюз с российским входом: у altrouter запросы идут на api.altrouter.ai, а к вендорам ходим мы.
Какие коды ошибок можно ретраить?
Повторять имеет смысл 429, 500, 502, 503 и 504: это временные состояния - лимит запросов, внутренний сбой шлюза, ошибка провайдера, отсутствие живого маршрута и таймаут. 400, 401, 402, 403, 404 и 409 повторять бесполезно: пока не исправлены тело запроса, ключ, баланс или скоуп, ответ будет тем же. При 429 ждите столько секунд, сколько указано в заголовке Retry-After, при 5xx - с экспоненциальной задержкой (1, 2, 4, 8 секунд) и ограничением по числу попыток. Для /v1/images и /v1/videos передавайте заголовок Idempotency-Key, чтобы повтор не создал вторую генерацию и второе списание.
Что означает ошибка 400 у AI-роутера?
400 - это invalid_request_error: тело запроса не прошло проверку до того, как его увидела модель. Поле param в ответе называет виновника: например «`messages` is required and must be a non-empty array.» с param «messages» или «`model` is required.» с param «model». Отдельный случай - 400 с типом content_policy_violation: запрос дошёл до модели, но контент отклонила модерация. Повторять 400 бессмысленно, пока запрос не исправлен.
Что значит ошибка 402 и как её убрать?
402 приходит с типом insufficient_quota и бывает двух видов. Первый - сообщение «Insufficient credits.» с кодом insufficient_quota: на балансе организации не хватает денег, чтобы зарезервировать стоимость запроса, лечится пополнением в кабинете. Второй - «This key has reached its month spend limit.» с кодом spend_limit_exceeded: сам ключ упёрся в свой потолок трат за день, неделю, месяц или за всё время. Во втором случае баланс может быть полным - поднимите или снимите лимит ключа либо дождитесь следующего окна.
Ошибка 429: превышен лимит запросов - что делать?
429 rate_limit_error означает, что ключ вышел за свои запросы в минуту: по умолчанию 600 rpm, у отдельного ключа может стоять свой лимит. В ответе есть заголовок Retry-After с числом секунд до конца окна, а также x-ratelimit-limit и x-ratelimit-remaining, по которым видно остаток. Правильная реакция - подождать Retry-After и повторить запрос; наращивать параллелизм поверх лимита бессмысленно. 429 от самого провайдера до вас не доходит: маршрутизатор переключает такой запрос на резервный маршрут.
Что значит «invalid credits amount»?
Такого текста altrouter не возвращает: нехватка денег у нас - это HTTP 402 с error.code insufficient_quota и сообщением «Insufficient credits.». Сообщение «invalid credits amount» отдаёт другой сервис, и относится оно к сумме пополнения, а не к запросу к модели, - проверьте сумму в его форме оплаты. Баланс altrouter пополняется в кабинете картой РФ, через СБП или криптой, и никакая сумма кредитов в теле API-запроса не передаётся.
Почему модель не вернула текст ответа?
Пустой ответ - это, как правило, не HTTP-ошибка: запрос отдаётся со статусом 200, а причина видна в choices[0].finish_reason и в usage.completion_tokens. Если ответ обрывается на полуслове, смотрите max_tokens: когда параметр не передан, подставляется 4096, и длинная генерация упирается в этот потолок. Если же контент отклонён, приходит именно ошибка - 400 с типом content_policy_violation. Обрыв уже начавшегося стрима попадает в логи кабинета с кодом stream_error: HTTP-статус к тому моменту уже ушёл клиенту как 200.
ДалееЛимиты и кредиты →