Коды ошибок

Отклонённый запрос приходит в общем конверте ошибки, и ветвиться в нём нужно по code — не по HTTP-статусу и никогда не по тексту сообщения. Неудачная генерация — отдельный случай: запрос при этом успешен, и причина лежит в другом поле и в другом словаре.

Три словаря, которые легко спутать

ПолеКогда приходитЧто называет
error.codeЗапрос отклонён, статус 4xx или 5xxПочему отклонён весь запрос. Словарь — таблица ниже.
error.details[].reasonРядом с error.code, когда отказ разбирается по полямЧем именно плохо названное в field значение. Словарь — таблица причин ниже.
error.reasonВнутри записи генерации — в любом успешном ответе, который её несёт: 200 у чтений и 202 у повтора отправкиПочему генерация со статусом FAILED не удалась: provider_error, moderation_block, timeout, no_content, invalid_input, unknown.

Один запрос несёт либо первый словарь, либо третий, никогда оба: успешный статус означает, что API ответил, даже если сама генерация провалилась. Поэтому клиент, который смотрит только на HTTP-статус, неудачных генераций не увидит вовсе.

Ветвиться на 200 здесь мало: повтор POST /v1/generations с тем же Idempotency-Key возвращает ту же самую запись и отвечает 202 независимо от её статуса — в том числе уже FAILED, вместе с её error.reason. Читайте это поле в любом ответе, который несёт запись генерации.

Форма ответа об ошибке

HTTP/1.1 400 Bad Request

{
  "error": {
    "code": "invalid_limit",
    "message": "The requested amount is out of range.",
    "user_message": "Запрошено больше допустимого.",
    "request_id": "8f3c1d2e-5a47-4b90-9a1e-2f6b0c7d4e11",
    "details": [
      {
        "field": "limit",
        "reason": "out_of_range",
        "message": "Too big: expected number to be <=100"
      }
    ]
  }
}
  • code — стабильный машинный токен. Это единственное поле, по которому клиенту стоит принимать решения.
  • message — всегда английский и предназначен для логов, а не для показа пользователю.
  • user_message — та же ошибка словами для человека. По умолчанию английская; русская приходит только на запрос с заголовком Accept-Language: ru, а если перевода для кода нет, поле равно message.
  • request_id — то, что нужно назвать в поддержке: по нему находится строка лога. Исключение — отказ по лимиту запросов без опознанного вызывающего: такие не пишутся в журнал доступа, и строки по этому идентификатору не будет.
  • details — появляется там, где у отказа есть причины по полям: field — путь до поля через точку, reason — что с этим полем не так, из словаря причин ниже, а message — та же причина текстом, для логов.

Ни стека, ни сообщения фреймворка в ответе не бывает. Если вы прислали X-Request-Id, request_id повторит его значение.

Чужой объект отвечает 404

Объект, принадлежащий другому аккаунту, отвечает 404, а не 403: строка, которую вызывающему нельзя видеть, читается как никогда не существовавшая. Поэтому 404 здесь не означает «нет прав» и не лечится повторами: проверяйте идентификатор, а не ключ.

Сообщить о проблеме

Когда генерация вернулась негодной, а error.reason ничего не объясняет, об этом сообщают: POST /v1/generations/{id}/report заводит обращение в поддержку и отвечает 201 с support_request_id и message_id — их и нужно называть в переписке. Маршруту нужна область generations:write.

Денег этот вызов не двигает. Возврат за такую генерацию — решение человека после разбора, и автоматической полосы здесь нет намеренно: баланс после отправки отчёта не меняется, и поля с кредитами в ответе нет.

  • text — обязателен, до 2000 символов.
  • reason — failure по умолчанию; moderation вдобавок отзывает саму генерацию — кроме той, что уже удерживается на разборе: её отзывает оператор, а обращение всё равно принимается.
  • client_msg_id — UUID, по желанию: повтор после потерянного ответа вернёт то же обращение, а не заведёт второе. Тот же идентификатор с другим текстом — 409 с кодом report_replay_mismatch.

Новое обращение с reason: failure по генерации, за которой не осталось неурегулированного — кредиты вернулись автоматически либо не списывались вовсе, — отвечает generation_already_settled: просить по ней нечего. Это не касается moderation — такое обращение принимается и по рассчитанной генерации — и повтора по тому же client_msg_id, который возвращает прежнее обращение. Чужой идентификатор, как и везде, отвечает 404.

Таблица кодов

Прочерк в последней колонке значит, что отдельного совета у кода нет.

КодСтатусmessageЧто делать
api_disabled503The public API is currently disabled.Публичный API отключён целиком — отвечает любой путь /v1, ещё до чтения ключа. Ключ и запрос здесь ни при чём, и повтор ничего не меняет.
unauthorized401Provide an API key or an OAuth access token via Authorization: Bearer, or a session cookie.Добавьте заголовок Authorization с API-ключом или OAuth-токеном; проверьте, что он не потерялся у прокси.
invalid_api_key401This API key is not valid.Ключ не разобран или не существует — выпустите новый.
api_key_expired401This API key has expired.Срок ключа истёк — выпустите новый.
api_key_revoked401This API key has been revoked.Ключ отозван — выпустите новый и замените его везде.
invalid_token401This access token is not valid, has expired, or its grant was revoked.OAuth-токен неизвестен, истёк, выпущен для другого ресурса или его разрешение отозвано. Обновите его по refresh-токену; отказал и refresh — проведите пользователя через авторизацию заново.
api_access_not_granted403This account does not have public API access.Аккаунту не выдан доступ к публичному API. Это не проблема ключа и не лечится повтором — обратитесь в поддержку.
account_suspended403This account is suspended.Обратитесь в поддержку — вызовы не пройдут ни по одному ключу.
access_denied403This request is not authorized for the current session.Приходит только на экране согласия OAuth: сессия браузера принадлежит не тому аккаунту, для которого начата авторизация. Начните авторизацию заново.
legal_consent_required403This account has not accepted the current terms. Sign in at coremind.ru and accept them; the same credentials work again afterwards.—
origin_mismatch403This request did not come from an allowlisted origin.Отказ бывает только на канале сессионной cookie и только на изменяющем запросе: страница прислала его с Origin, которого нет в списке разрешённых. Вызову по API-ключу этот код не приходит вовсе. Отправляйте такой запрос со страницы самого сервиса — или, если это ваша интеграция, ключом со своего сервера, а не из браузера.
insufficient_scope403This credential is missing a scope required by the endpoint.Ключу или OAuth-токену не хватает области, которую требует маршрут, — добавьте её в настройках ключа в профиле или запросите новой авторизацией.
payment_required402This account has no active paid plan, so its API credentials may neither start generations — free quotas and promo modes included — nor cancel them. Buy or renew a plan on coremind.ru; nothing else unlocks it.У аккаунта нет активного тарифа, поэтому через API закрыты и запуск генераций — платный, по бесплатным квотам и промо, — и их отмена. Прошлые оплаты не считаются, ключ здесь ни при чём — оформите или продлите тариф в интерфейсе. Точный повтор уже принятого запроса с тем же Idempotency-Key по-прежнему возвращает сохранённый ответ.
insufficient_credits402The account balance does not cover this request.Пополните баланс. Сколько стоит запрос, заранее скажет POST /v1/pricing/estimate.
insufficient_text_credits402The chat balance does not cover this message.—
context_limit_exceeded400The conversation is longer than the chosen model accepts.—
duplicate_submission409This request was already submitted. Send it again with the confirm token to repeat it.—
duplicate_key_conflict409This Idempotency-Key was already used with a different request body.Этот Idempotency-Key уже закреплён за другим телом запроса. Повтор не поможет — отправляйте новый ключ.
moderation_blocked403The request was blocked by content moderation.Запрос отклонён модерацией. Повтор того же текста или той же картинки даст тот же ответ.
submit_unavailable503This generation type is not submitted through this API.Этот тип генерации API не принимает. Запрос корректен, ничего не списано, и повтор даст тот же ответ. Модели такого типа не показываются в GET /v1/models, поэтому модель, выбранная оттуда, этот ответ не получит. Оценка цены для такого типа отвечает тем же кодом.
upload_unavailable503This upload kind is not signed through this API.Такой kind не подписывается: загрузка принимает image.
estimate_unavailable422This request cannot be priced before submitting: a promo_mode submit is charged by entitlement, not by a rate.Запрос с promo_mode оплачивается правом на промо-режим, а не тарифом, поэтому цену заранее не узнать. Без promo_mode тот же запрос оценивается.
key_spend_limit_reached402This API key has reached its monthly spend limit.—
grant_spend_limit_reached402This authorized application has reached its monthly spend limit.—
generation_frozen409This generation is held for review and cannot be settled yet.Генерация придержана для разбора — ни отменить, ни удалить её нельзя.
generation_in_flight409This generation is already running and can no longer be cancelled.Генерацию уже забрали в работу: отменять поздно, дождитесь результата.
generation_already_settled422Nothing is owed for this generation: its credits were refunded automatically or were never charged. There is nothing to report.По этой генерации не осталось ничего неурегулированного: кредиты уже вернулись автоматически либо не списывались вовсе — например на бесплатной квоте или в промо-режиме. Просить нечего.
report_replay_mismatch409This client_msg_id was already used for a different report body.Этот client_msg_id уже закреплён за другим обращением: сравнивается и text, и reason. Повтор с тем же идентификатором должен нести то же тело; для другого обращения возьмите новый UUID.
support_replay_mismatch409This client_msg_id was already used for a different message.Этот client_msg_id уже закреплён за другим сообщением в переписке с поддержкой: сравниваются text и число картинок. Повтор с тем же идентификатором должен нести то же тело; для нового сообщения возьмите новый UUID.
folder_limit_reached409This account already holds the maximum number of library folders. Delete one before creating another.Папок уже столько, сколько разрешено на аккаунт. Удалите ненужную папку — её содержимое уйдёт в корень — и повторите.
folder_name_taken409A library folder with this name already exists.Папка с таким названием уже есть. Выберите другое название или работайте с существующей папкой.
not_found404No such endpoint or resource.Проверьте путь и идентификатор. Чужой объект отвечает тем же кодом, что и несуществующий.
unreadable_record422A stored record for this resource could not be read back.—
revision_conflict409This resource was changed by another request. Reload it before saving again.—
resource_conflict409This resource could not be saved in its current state.—
gone410This endpoint has been retired and is no longer available.Маршрут выведен из эксплуатации — перейдите на его замену.
plan_expired400The paid period has ended; buy the plan again.—
renewal_already_on400Auto-renewal is already on.—
no_active_plan400This account has no active plan.—
subscription_not_linked409No provider subscription is linked to this account.—
card_token_missing409No saved card is left to renew with; buy the plan again.—
checkout_in_progress409A plan checkout is still open; finish it first.—
subscription_superseded409A newer subscription already renews this plan.—
subscription_recancelled409The subscription was cancelled again; resume it once more.—
subscription_resume_failed502The subscription could not be resumed.—
subscription_cancel_failed502The subscription could not be cancelled at the provider.—
subscription_cancel_unsaved500The subscription was cancelled at the provider but the local state was not saved.—
card_change_unavailable409This subscription cannot change its card.—
card_check_not_authorized409The card check did not authorize; try again.—
card_change_in_progress409This card change is already being completed.—
card_change_failed502The card could not be changed.—
rate_limited429Too many requests. Retry after the window resets.Читайте details[].reason: request_rate — квота HTTP-запросов, submit_rate — отправка генераций, concurrency — занятые места, queue_full — заполненная очередь. Для минутных окон соблюдайте Retry-After; при занятых местах дождитесь завершения задачи. Поля scope, limit и current уточняют ограничение. Заголовки RateLimit всегда описывают HTTP-окно. Другие лимиты маршрутов могут возвращать только общий код — тогда повторяйте с растущей паузой.
too_many_streams429This key already holds the maximum number of open event streams. Close one before opening another.—
request_too_large413The request body is larger than this API accepts.—
internal_error500Unexpected server error.Повторите с выдержкой. Если повторяется — напишите в поддержку и приложите request_id.
service_unavailable503The service is temporarily unavailable. Retry shortly.—
request_aborted499The client closed the request before it was answered.—
invalid_request400The request parameters are not valid.Исправьте запрос по details: там названы поле и причина.
invalid_limit400The requested amount is out of range.Приведите limit к допустимым границам — целое от 1 до 100 — или сократите список идентификаторов в теле запроса до 50.
invalid_cursor400The cursor parameter is not a valid cursor.Не правьте и не собирайте курсор сами. Передавайте next_cursor без изменений или начните с первой страницы.

Причины в details

Это полный словарь error.details[].reason: он собирается из кода, который эти причины и отдаёт, поэтому новая причина появляется в таблице вместе с собой. Ветвиться по нему можно — токены стабильны, — но решение об ошибке в целом всё равно принимается по error.code.

reasonЧто означает
wrong_typeУ поля не тот тип, который описан в схеме.
not_allowedЗначение вне набора допустимых.
bad_formatЗначение не того формата — идентификатор, URL, дата.
out_of_rangeЧисло, длина или количество вне допустимых границ.
unknown_fieldТакого поля в запросе быть не должно.
duplicateЗначение повторяется там, где повторы запрещены.
invalidОбщий случай: значение не подошло, а отдельного имени у причины нет.
requiredОбязательное поле или заголовок не передан.
in_useЗначение уже закреплено за другим запросом — например Idempotency-Key.
exceeded—
request_rate—
submit_rate—
concurrency—
queue_full—
not_availableСамо значение допустимо, но этот путь его не принимает.
unknown_modelЭтой модели нет в GET /v1/models для вашего ключа: такой модели нет, этот путь не отправляет её тип, она выключена или доступна только в раннем доступе.
unsupported_modelМодель есть, но она не подаётся через этот тип запроса.
unsupported_resolutionМодель не публикует такое разрешение.
unsupported_durationМодель не публикует такую длительность — в том числе в паре с выбранным разрешением.
unsupported_aspectМодель не публикует такое соотношение сторон.
unsupported_qualityУ модели нет оси качества или такого значения на ней.
unsupported_scaleМодель не публикует такой коэффициент увеличения.
unsupported_n_variantsМодель рендерит меньше выходов, чем запрошено.
transparency_not_supportedМодель не умеет прозрачный фон.
prompt_too_longПромпт длиннее предела, который публикует модель.
image_requiredМодели нужны референсы, а их меньше минимума.
too_many_imagesРеференсов больше, чем модель принимает.
refused—
extend_not_found—
extend_unavailable—
draft_missing—
draft_not_found—
draft_wrong_model—
draft_not_ready—
draft_cache_missing—
draft_expired—
draft_facts_missing—
untrusted_reference—
invalid_graph—
insufficient_credits—
foreign_reference—
reference_unavailable—
unfetchable_reference—

Сколько запросов осталось в окне, как долго ждать после rate_limited и какие границы у limit — на странице лимитов. Статусы каждой отдельной операции перечислены в справочнике.