Ошибки
Формат ответа об ошибке, таблица всех кодов, что делать в каждом случае.
Все ошибки Hubris API возвращаются в OpenAI-совместимом формате. Это значит — стандартные SDK обрабатывают их без специальной адаптации.
Формат
{
"error": {
"message": "Описание ошибки на английском",
"type": "<категория ошибки>",
"code": "<машинно-читаемый код>"
}
}message— человекочитаемое сообщение (на английском, для совместимости с OpenAI-инструментами).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.hubris.pw/v1",
default_headers={"X-No-Ads": "1"},
)В первой версии поле не добавляется в SSE-чанки, ответы /v1/messages,
ошибки 401/403 и 5xx, а также не-JSON ответы. Ошибка до открытия
стрима остаётся обычным JSON-ответом и может содержать error.ad.
Таблица кодов
| HTTP | code | Что значит | Что делать |
|---|---|---|---|
| 400 | invalid_request | Тело запроса не соответствует схеме (пропущенное поле, неверный тип, выход за допустимые границы) | Проверить JSON по Быстрому старту |
| 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_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 | video_not_ready | Запрошен /content видео-задачи, которая ещё не completed (или истёк 72-часовой TTL) — только /v1/videos | Опрашивать GET /v1/videos/{id} до status: "completed", затем качать /content |
| 400 | index_out_of_range | Параметр index в /content вне диапазона (v1 отдаёт только index=0) — только /v1/videos | Использовать index=0 |
| 502 | upstream_error | Провайдер модели вернул ошибку (5xx, контент-фильтр, internal error) | Повторить через несколько секунд; если повторяется — попробовать другую модель |
| 503 | exchange_rate_unavailable | Курс ЦБ РФ временно недоступен — каталог не отдаёт цены | Повторить через минуту |
| 504 | upstream_timeout | Провайдер модели не ответил за 120 секунд | Повторить; если повторяется — модель перегружена, попробовать другую |
Примеры
401 (нет ключа)
Запрос: curl https://api.hubris.pw/v1/models
{
"error": {
"message": "Missing API key",
"type": "invalid_request_error",
"code": "invalid_api_key"
}
}402 (баланс ноль)
{
"error": {
"message": "Insufficient balance",
"type": "invalid_request_error",
"code": "insufficient_balance"
}
}404 (модели не существует)
Запрос с несуществующим идентификатором модели:
{
"error": {
"message": "Model not found: fake-provider/nonexistent",
"type": "invalid_request_error",
"code": "model_not_found"
}
}502 (апстрим лёг)
{
"error": {
"message": "Upstream returned an error",
"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 — почти всегда баг в коде. Логируйте полный запрос (без ключа) для разбора.
Стриминг
При stream: true и ошибке ДО начала стрима возвращается стандартный JSON-ответ выше.
Если ошибка случается во время стрима (модель упала на середине) — стрим обрывается на серверной стороне. Клиент получает событие [DONE] с пустым delta и finish_reason: "stop". Точное поведение зависит от провайдера. Hubris записывает в логи ваш конкретный сценарий, чтобы вы могли разобраться через /usage.
Что дальше
- Быстрый старт — рабочий пример без ошибок.
- Аутентификация — про 401.
- Биллинг — про 402.
Обновлено: