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

Ошибки

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

Открыть как Markdown

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

Отклонённый вызов отвечает одним 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Запрос не аутентифицирован.
403Ключу не разрешено вызывать эту модель, либо организация заморожена.
404Такой модели нет.
429Запрос отклонён лимитом темпа или лимитом трат.
500Запрос не удалось выполнить.
503Ни один провайдер сейчас не может обслужить этот запрос.

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

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

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

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

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

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

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

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

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

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

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

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

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

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