Перейти к содержимомуKumoДокументация
Разделы
На странице
Шлюз

Ошибки

Конверт, в котором приходит отказ, коды ответа шлюза и два поведения, которые должен учитывать клиент.

Открыть как Markdown

Быстро

Вот вся таблица решений. Подробности каждой строки — в разделах ниже.

КодПричинаЧто делать
400Запрос невалиден: искажённое тело или член, которого эта поверхность не принимает.Починить тело. Повторять нечего — тот же запрос отвергнется так же.
401Запрос не аутентифицирован.Проверить заголовок и сам ключ. Не повторять.
402Платить нечем: кошелёк пуст или исчерпан пакет, из которого тратит ключ.Пополнить кошелёк или пакет, либо переключить ключ на баланс. Не повторять.
403Ключу не разрешено вызывать эту модель, либо организация заморожена.Проверить область ключа и состояние организации в консоли. Не повторять.
404Такой модели нет.Сверить имя с опубликованным каталогом. Не повторять.
429Вызов вышел за потолок темпа или трат.Отступить и повторить позже, удлиняя паузу с каждой попыткой и ограничив их число.
500Запрос не удалось выполнить.Отступить и повторить не сразу. Вслепую в цикле — нет: исход наверху мог остаться неизвестным.
503Ни один провайдер сейчас не может обслужить этот запрос.Отступить и повторить позже. Немедленный повтор не помогает: наверху ничего не изменилось.

Ни один отказ не лечится немедленным повтором. Клиент, который считает любую ошибку поводом «попробовать сейчас же», тратит на отказы всю свою квоту.

Конверт отказа

Отклонённый вызов отвечает одним JSON-объектом с единственным членом error. Это собственный конверт протокола, а не перевод чужого: одна и та же форма отказывает вызову Chat Completions, вызову Responses и вызову Messages, а поверхность Messages оборачивает её верхнеуровневым "type": "error", которого ждут её клиенты.

Два члена есть всегда — type и message; ещё два появляются, когда есть что сказать.

typeКласс отказа в собственном закрытом словаре протокола: invalid_request_error, not_found_error, authentication_error, permission_error, rate_limit_error, api_error, а на поверхности Messages ещё и overloaded_error.
messageЧто пошло не так — фиксированной безопасной фразой. Она никогда не собирается из того, что нёс запрос, поэтому не может процитировать в лог ни промпт, ни заголовок, ни ключ.
codeМашиночитаемая причина. У одной причины одно написание на всех поверхностях платформы — потому именно на неё и ветвится клиент.
paramЧлен запроса, в котором дело, — когда дело в одном члене.

Ветвитесь на type и на code, но никогда — на формулировке message: она выбрана так, чтобы её было безопасно печатать, а не так, чтобы её разбирали.

{
  "error": {
    "type": "authentication_error",
    "message": "The request is not authenticated."
  }
}

Коды ответа

400Запрос невалиден: искажённое тело или член, которого эта поверхность не принимает.
401Запрос не аутентифицирован.
402Платить нечем: кошелёк пуст (`insufficient_wallet_balance`) или пакет ключа исчерпан (`insufficient_package_balance`). Отказ решается до обращения к провайдеру.
403Ключу не разрешено вызывать эту модель, либо организация заморожена.
404Такой модели нет.
429Запрос отклонён лимитом темпа или лимитом трат.
500Запрос не удалось выполнить.
503Ни один провайдер сейчас не может обслужить этот запрос.

Два из них стоит различать до того, как написан повтор. 429 — это потолок, выше которого вы оказались, и лечит его время; 503 означает, что сейчас вызов не может обслужить никто наверху. Ни один из них не лечится немедленным повтором, а клиент, считающий любой отказ поводом «повторить сейчас же», тратит на отказы всю свою квоту.

Один 401 на любую беду с ключом

На отсутствующий, искажённый, неизвестный, отозванный и истёкший ключ ответ одинаковый — тот же 401 и та же фраза, — и это решение, а не недоделка. Различать их значило бы превратить шлюз в оракул по ключевому материалу: тот, кто видит разницу между «такого ключа нет» и «этот ключ отозван», читает таблицу ключей платформы по одной догадке за раз.

Поэтому 401 говорит ровно одно: вызов не допущен. Проверьте, что заголовок на месте и записан как bearer-токен, что ключ — это целиком тот секрет, который вы скопировали при выпуске, и что ключ ещё жив. Проверка ключа отвечает на все три вопроса сразу, из браузера и без единой строки кода.

Проверить ключ → Как пишется заголовок →

Когда поток обрывается раньше срока

Отказ, решённый до первого события, приходит обычной JSON-ошибкой со статусом — ровно так же, как ответили бы на неразбитый на события вызов: тот самый конверт, и ничего в потоке. Сбой после начала потока таким быть уже не может: строка статуса ушла раньше. Такой поток просто заканчивается — без завершающего кадра [DONE].

Этим отсутствием всё и сказано, поэтому читать нужно именно его. Клиент, который считает, что «соединение закрылось» значит «ответ дописан», отдаст дальше усечённый ответ, и в полученных им байтах ничто не скажет обратного. Считайте поток, кончившийся до [DONE], неудавшимся вызовом, а не коротким.

Две другие поверхности говорят то же своими событиями. На поверхности Messages сбой приходит событием ошибки этого протокола, и дальше поток кончается без message_stop; на поверхности Responses завершающее событие — response.failed, несущее этот конверт вместо готового тела.

Переключение

До модели, как правило, есть больше одного канала. Когда обслуживающий вызов канал отказывает до того, как до вас дошла хоть часть ответа, вызов продолжается на следующем канале той же модели, а не возвращается вам.

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

Поэтому 503 не обещает, что перепробованы все маршруты. Он приходит и тогда, когда ни один канал не подходит под то, о чём просил вызов, — ещё до обращения наверх, — а число каналов, которые переберёт один вызов, ограничено и перебор не является исчерпывающим.

Чем ограничена пропускная способность →