Ошибки и лимиты

Обновлено

Скачать Markdown

Отказ приходит в одной и той же форме на всех ручках, а по заголовкам ответа видно, сколько запросов осталось и когда повторять. Эта статья описывает то и другое.

Форма отказа

{
  "error": {
    "code": "cost_above_limit",
    "message": "The generation costs more than the maxCredits ceiling in the request.",
    "retryable": false,
    "details": { "credits": 62, "maxCredits": 40 }
  }
}

code — машинный идентификатор, единственное, на что стоит программировать. message написан для человека, читающего лог, и может меняться. retryable отвечает, имеет ли смысл повторить тот же запрос без изменений. details есть не всегда: там лежит машинная диагностика — перечень проблем валидации, незакрытые входы, числа.

Локализации нет: ответ читает программа, а не человек в интерфейсе.

Каждый ответ несёт X-Request-Id. Он же записан в наших логах, поэтому разбор происшествия начинается с одной строки — приложите его к обращению в поддержку.

Коды и статусы

Список закрыт: код, которого нет в таблице, наружу не уходит. Новые значения могут появляться — незнакомый код обрабатывайте по числовому статусу и признаку retryable из ответа.

КодHTTPКогда приходит
access_denied403Внешний доступ этому счёту закрыт
account_blocked403Аккаунт заблокирован
cancel_conflict409Состояние генерации изменилось во время отмены
cost_above_limit402Запрос дороже переданного maxCredits
credit_reservation_failed402Резерв кредитов не удался
family_not_found400Такого семейства в каталоге нет
family_unavailable503Семейство временно недоступно
file_fetch_failed502Файл по ссылке не забрался
file_metadata_missing400Длительность входного видео ещё не измерена, а модель считается посекундно
file_not_found404Такого идентификатора файла или генерации нет
file_not_ready409Файл ещё обрабатывается
file_processing_failed422Обработка файла не удалась
generation_not_found404Генерации с таким идентификатором у вас нет
idempotency_conflict409Idempotency-Key уже использован с другими параметрами
idempotency_in_progress409Запрос с этим Idempotency-Key ещё выполняется
insufficient_credits402Кредитов не хватает — на балансе или после вычета занятого другими запусками
insufficient_scope403У ключа нет нужного права
internal_error500Внутренний сбой сервиса
invalid_cursor400Метка страницы не из предыдущего ответа
invalid_parameters400Параметры не подходят выбранному варианту
invalid_request400Тело или параметр не прошли проверку; перечень проблем в details.issues
invalid_token401Ключ отозван или не существует
not_cancellable409Генерация запущена в редакторе — такую отменяют там
payload_too_large413Тело запроса больше допустимого
price_unavailable400Стоимость для таких параметров не складывается
queue_unavailable500Очередь генерации недоступна
rate_limited429Превышена частота запросов
service_unavailable503Сервис временно недоступен
spend_limit_exceeded403Задет потолок ключа; какой именно — в details.limitKind
subscription_required403Семейство требует действующей подписки
team_expired403Срок плана команды истёк
team_required403Доступ идёт вместе с командой; чего не хватает — в тексте ошибки
too_many_concurrent429Слишком много запросов в работе одновременно
unauthorized401Ключа в запросе нет
unsupported_content_type415Такой тип содержимого ручка не принимает
unsupported_file_format415Содержимое не опознано или такой формат не принимается
variant_not_resolved400Ни один вариант не подходит к такому набору входов

Повторять или нет, говорит retryable в самом ответе: у части кодов это зависит от причины, а не от кода. У всех отказов по частоте и временной недоступности есть заголовок Retry-After.

Отказ самой генерации

Провалившаяся генерация — это ответ 200 с состоянием failed, а не отказ запроса: запрос выполнен, не удалась работа модели. Причина лежит внутри генерации и устроена иначе, чем отказ ручки:

{
  "generationId": "8f14e45f-ceea-467a-9a3e-4b1c2d5e7f80",
  "state": "failed",
  "error": {
    "code": "prompt_validation",
    "reason": "prompt_too_long",
    "message": "The prompt is longer than this model accepts.",
    "retryable": false,
    "hint": "The model could not work with this prompt. Rewrite it, or pick another family.",
    "details": { "limit": 8000 }
  }
}

code — класс отказа, список закрыт. hint называет действие, а не диагноз: что менять перед следующей попыткой. details появляется там, где из отказа удалось достать числа — например предел длины промпта у конкретной модели.

КлассЧто случилосьЧто делать
content_policyМодель отказала по содержаниюМенять промпт или входные файлы: тот же запрос откажет так же
prompt_validationМодель не приняла промптПереписать промпт или взять другую модель
parameter_validationПараметр или вход не подходит моделиСвериться со схемой семейства и поправить запрос
file_sizeВходной файл не укладывается в пределы моделиВзять файл другого размера
image_sizeРазмер входной картинки не подходит моделиВзять картинку другого размера
image_formatФайл не прочитался как изображениеЗагрузить заново в поддерживаемом формате
feature_limitationМодель не умеет того, о чём просилиВыбрать другое семейство
model_unavailableМодель недоступнаВзять другое семейство или повторить позже
insufficient_creditsКредиты кончились до стартаПроверить баланс перед повтором
authenticationДоступ к модели временно закрытПовторить позже; запрос ни при чём
timeoutМодель не ответила вовремяПовторить: тот же запрос может пройти
provider_errorВременный сбой моделиПовторить: тот же запрос может пройти
internal_errorВнутренний сбой сервисаПовторить: тот же запрос может пройти
user_cancellationГенерацию отменилиНичего — это ответ на отмену

reason — причина внутри класса, и программировать разное поведение стоит по ней. Один класс покрывает несколько разных отказов: prompt_too_long, prompt_failed и prompt_images_failed — всё это prompt_validation, но первый чинится обрезкой промпта, а последний — заменой входных изображений. Список причин, в отличие от списка классов, открыт и пополняется. Опубликованную причину мы не переименовываем, а незнакомую обрабатывайте по code.

Дословную формулировку модели мы наружу не отдаём — в message приходит наш текст по причине отказа. Чтобы подняли оригинал, назовите generationId в обращении в поддержку.

Причина unclassified при классе internal_error означает, что отказ не удалось отнести ни к одной известной причине. Обрабатывайте его по code; со временем такие отказы получают точную причину.

Частота и одновременность

Ограничений два, и считаются они на пользователя, а не на ключ: сорвавшийся скрипт одного участника команды не должен закрывать API остальным.

Частота. Сейчас это 600 запросов в минуту. Превышение — 429 rate_limited с заголовком Retry-After.

Одновременность. До 32 запросов в работе одновременно. Потолок рассчитан на ожидание через wait, где запрос висит до минуты; больше тридцати двух открытых ожиданий — повод перейти на историю или на вебхуки. Превышение — 429 too_many_concurrent.

Точные числа приходят в заголовках каждого ответа, и опираться стоит на них, а не на значения в тексте:

X-RateLimit-Limit: 600
X-RateLimit-Remaining: 587
X-RateLimit-Reset: 1785974160

X-RateLimit-Reset — момент обнуления счётчика в секундах Unix.

Отдельно от этого действуют потолки траты самого ключа — суточный и разовый. Они описаны в статье Ключ и права.

Потолки размеров

ЧтоПределПри превышении
Тело запроса256 КиБ413 payload_too_large
Файл25 МБ — одинаково для multipart/form-data, ссылки и base64Отказ приёма файла
Промпт20 000 символовОтказ проверки запроса
Медиа-входов в запросе20Подбор варианта отказывает
Страница истории50 записейЗначение больше не принимается
Ожидание внутри запроса60 секундОтвет приходит с pollAfterSeconds

Две последние границы — заслон транспорта, а не пределы моделей: те заметно строже. Сколько входов принимает вариант, видно в подробностях семейства (inputs.images.max и соседние поля), а предел длины промпта — в описании параметра, если модель его объявляет. Лишний файл отвергается подбором варианта с кодом variant_not_resolved, слишком длинный промпт — самой моделью с классом prompt_validation, и там же в details приходит настоящее число.

Машинное описание

GET /api/v1/openapi.json отдаёт спеку OpenAPI: пути, схемы запросов и ответов, коды отказа. Она собирается из тех же схем, которыми проверяется вход, поэтому разойтись с поведением не может.

Спека закрыта тем же ключом, что и остальные ручки, и требует права catalog:read: документ описывает ручки, но не даёт ими пользоваться.