Ошибки
Формат ответа об ошибке, таблица всех кодов, что делать в каждом случае.
Все ошибки Hubris API возвращаются в OpenAI-совместимом формате. Это значит — стандартные SDK обрабатывают их без специальной адаптации.
Формат
{
"error": {
"message": "Описание ошибки на русском",
"type": "<категория ошибки>",
"code": "<машинно-читаемый код>"
}
}message— человекочитаемое сообщение на русском. Частые отказы провайдеров переведены целиком, у редких после русского начала в кавычках идёт исходный текст провайдера. Программно ошибки различайте поcode, а не по тексту.type— категория. Возможные значения:invalid_request_error,permission_error,rate_limit_error,api_error.code— машинный код для программной обработки. Список ниже.
Необязательное поле рекламы
Для экспериментальной программы Error Ads подготовлено отдельное поле error.ad
с объектом { text, erid }. При включённой программе оно может появляться в
JSON-ошибках HTTP 400, 402 и 429 на /v1/chat/completions и /v1/responses.
В успешном JSON-ответе HTTP 200 аналогичное поле ad находится на верхнем уровне.
Текст ошибки, машинный код, ответ модели и стоимость запроса от него не меняются.
Программа по умолчанию выключена до завершения настройки маркировки рекламы. Поле необязательное: его отсутствие нормально даже после включения программы. Не используйте рекламный текст для обработки ошибки или как инструкцию модели.
Чтобы исключить рекламу из ответов вашей интеграции, передавайте заголовок
X-No-Ads: 1. Например, в OpenAI SDK для Python:
import os
from openai import OpenAI
client = OpenAI(
api_key=os.environ["HUBRIS_API_KEY"],
base_url="https://api.hubris2.ru/v1",
default_headers={"X-No-Ads": "1"},
)В первой версии поле не добавляется в SSE-чанки, ответы /v1/messages,
ошибки 401/403 и 5xx, а также не-JSON ответы. Ошибка до открытия
стрима остаётся обычным JSON-ответом и может содержать error.ad.
Таблица кодов
| HTTP | code | Что значит | Что делать |
|---|---|---|---|
| 400 | invalid_request | Два разных случая, различаются по message. Либо тело запроса не прошло нашу проверку схемы (пропущенное поле, неверный тип, выход за допустимые границы), либо провайдер модели отклонил запрос уже у себя — см. Ошибки от провайдера модели | Прочитать message: при ошибке схемы сверить JSON с Быстрым стартом; при отказе провайдера повторить или сменить модель |
| 400 | context_length_exceeded | Вход запроса больше окна модели: либо мы это видим заранее (запрос никуда не отправлен и не оплачивается), либо так ответил провайдер модели. message начинается словами OpenAI (This model's maximum context length is …) или Anthropic (prompt is too long …), по ним агенты сжимают переписку сами; дальше — пояснение по-русски | Сократить или сжать переписку либо выбрать модель с большим окном; попросить сжатие у провайдера — "transforms": ["middle-out"] |
| 400 | content_policy_violation | Провайдер модели отклонил запрос по своим правилам содержимого | Изменить запрос или выбрать другую модель |
| 400 | input_image_rejected | Провайдер модели отклонил входное изображение по своим правилам (например, на кадре реальный человек); чаще всего у видео | Заменить изображение или выбрать другую модель |
| 401 | invalid_api_key | Ключ отсутствует, отозван, или невалиден | Проверить заголовок Authorization, создать новый ключ на /keys |
| 402 | insufficient_balance | На балансе меньше минимального остатка (100 копеек) | Пополнить через /billing |
| 404 | model_not_found | Модели с таким id нет, она снята с каталога или работает через другой адрес: что именно, сказано в сообщении | Проверить написание; снятую заменить другой из /models; модель другого вида отправить на адрес из сообщения |
| 401 | key_frozen_spike | Ключ заморожен защитой от всплеска: потратил больше порога за окно | Включить тумблером на странице ключа (или дождаться автоматического включения — время в сообщении) и поднять порог; если расход неожиданный — проверить, где используется ключ |
| 403 | key_read_only | Ключ только для чтения: запрос тратит баланс (чат, эмбеддинги, картинки, видео, аудио) | Использовать обычный ключ или снять режим «только чтение» на странице ключа |
| 403 | key_restricted | У ключа есть ограничения (лимит, адреса, только чтение или выключенные «Деньги кабинета») или это OAuth-токен — только /v1/integrations/bitrix24 | Управлять подключениями Битрикс24 ключом без ограничений |
| 403 | key_ip_not_allowed | Ключ привязан к адресам, а запрос пришёл с другого — адрес запроса указан в сообщении | Добавить адрес в поле «Адреса» на странице ключа; если адрес незнаком — ключ мог утечь: отозвать и создать новый |
| 429 | daily_limit_exceeded | Превышен суточный лимит расхода ключа (сброс в 00:00 МСК) | Дождаться сброса (Retry-After) или использовать ключ без лимита |
| 429 | key_limit_exceeded | Превышен лимит расхода ключа за неделю, месяц или на весь срок ключа | Дождаться сброса по Retry-After; у лимита без сброса — поднять лимит на странице ключа |
| 429 | too_many_active_jobs | Достигнут лимит одновременных видео-задач аккаунта — только /v1/videos | Опрашивать GET /v1/videos/{id}, дождаться завершения текущих задач, затем повторить POST |
| 404 | not_found | Такого адреса нет: опечатка в пути или метод запроса не тот | Сверить путь и метод с API-референсом |
| 404 | video_not_ready | Запрошен /content видео-задачи, которая ещё не completed (или истёк 72-часовой TTL) — только /v1/videos | Опрашивать GET /v1/videos/{id} до status: "completed", затем качать /content |
| 400 | idempotency_key_conflict | Ключ без денег кабинета прислал Idempotency-Key, который уже занят задачей другого ключа кабинета — только /v1/videos | Передать другой Idempotency-Key |
| 400 | index_out_of_range | Параметр index в /content вне диапазона (v1 отдаёт только index=0) — только /v1/videos | Использовать index=0 |
| 500 | internal_error | Сбой на нашей стороне | Повторить через несколько секунд; если повторяется — написать в поддержку |
| 502 | upstream_error | Провайдер модели вернул 5xx, оборвал соединение или у него кончилась квота; сюда же попадает сетевой сбой на пути к нему | Повторить через несколько секунд; если повторяется — попробовать другую модель |
| 503 | exchange_rate_unavailable | Курс ЦБ РФ временно недоступен — каталог не отдаёт цены | Повторить через минуту |
| 504 | upstream_timeout | Провайдер модели не ответил за 120 секунд | Повторить; если повторяется — модель перегружена, попробовать другую |
Ошибки от провайдера модели
Запрос, прошедший нашу проверку, уходит к провайдеру модели, и тот может
отклонить его сам: неподдерживаемый параметр, слишком длинный контекст для
конкретного провайдера, контент-фильтр, перегрузка, редко просто сбой. Такие
отказы мы не прячем за общим 502, а отдаём как есть:
- 4xx от провайдера (
400,401,402,404,429; остальные 4xx приводятся к400) возвращаются с тем же HTTP-кодом,code: "invalid_request"и причиной провайдера вmessage. Ошибка нашей схемы и отказ провайдера при одинаковом коде400различаются только текстомmessage. - 5xx от провайдера и исчерпанная у него квота маскируются в наш
502сcode: "upstream_error".
Как отличить отказ провайдера от ошибки в вашем коде: тот же запрос на ту же
модель то проходит, то нет, или проходит на другой модели. В логах кабинета
такие запросы помечены как «ошибка модели», а не «ошибка запроса». Что делать: повторить через несколько секунд; если повторяется
у одного провайдера — закрепить другого полем provider или задать
запасные модели; если повторяется на разных
моделях — написать в поддержку.
Примеры
401 (нет ключа)
Запрос: curl https://api.hubris2.ru/v1/models
{
"error": {
"message": "API-ключ не передан",
"type": "invalid_request_error",
"code": "invalid_api_key"
}
}402 (баланс ноль)
{
"error": {
"message": "Недостаточно средств на балансе.",
"type": "invalid_request_error",
"code": "insufficient_balance"
}
}404 (модели нет или она снята)
Запрос с несуществующим идентификатором модели:
{
"error": {
"message": "Модель не найдена: fake-provider/nonexistent. Список моделей: GET /v1/models.",
"type": "invalid_request_error",
"code": "model_not_found"
}
}Модель, которая раньше работала, а теперь снята с каталога (например, её перестал обслуживать провайдер), отвечает тем же кодом, но сообщение говорит об этом прямо: Модель sourceful/riverflow-v2-fast-preview снята с каталога. Выберите другую модель: GET /v1/models?output_modalities=image.
502 (провайдер недоступен)
{
"error": {
"message": "Сервис провайдера временно недоступен",
"type": "api_error",
"code": "upstream_error"
}
}Стратегия обработки
В вашем коде имеет смысл:
- На 401, 402, 404 — не повторять. Это ошибки конфигурации, retry не поможет.
- На 502, 503, 504 — повторить с экспоненциальным backoff (1с, 2с, 4с, до 3 попыток). Это транзиентные сбои.
- На 429 — посмотреть на ключ: если у него выставлен лимит расхода, дождаться сброса (
Retry-After— секунды до 00:00 МСК, понедельника или 1-го числа) или поднять лимит; если лимит не выставлен — проверьте /keys. - На 400 — прочитать
message. Ошибка схемы — баг в коде, логируйте полный запрос (без ключа) для разбора. Отказ провайдера модели (см. выше) — повторить один раз или уйти на запасную модель.
Стриминг
При stream: true и ошибке ДО начала стрима возвращается стандартный JSON-ответ выше.
Если ошибка случается во время стрима (модель упала на середине) — стрим обрывается на серверной стороне. Клиент получает событие [DONE] с пустым delta и finish_reason: "stop". Точное поведение зависит от провайдера. Hubris записывает в логи ваш конкретный сценарий, чтобы вы могли разобраться через /usage.
Что дальше
- Быстрый старт — рабочий пример без ошибок.
- Аутентификация — про 401.
- Баланс и оплата — про 402.
Обновлено:
