# Ошибки и лимиты запросов

> Все ошибки API OpenCheese: формат JSON для каждого эндпоинта, коды статусов и ошибок, заголовки лимитов запросов и какие ошибки стоит повторять.
>
> Веб-страница: https://opencheese.dev/ru/docs/errors/ · Все страницы документации: https://opencheese.dev/ru/docs.md

Ошибка приходит как HTTP-статус с телом в JSON в формате того эндпоинта, который вы вызвали.
Каждое сообщение начинается с 🧀, чтобы сообщение OpenCheese можно было отличить от текста модели,
а в каждом ответе есть заголовок `x-request-id`: укажите его, когда обращаетесь в поддержку.

## Формат ошибок

Эндпоинты формата OpenAI (`/v1/chat/completions`, `/v1/responses`, `/v1/images/*`, `/v1/models` и
эндпоинты аккаунта):

```json
{
  "error": {
    "message": "🧀 Invalid or disabled OpenCheese API key.",
    "type": "authentication_error",
    "code": "invalid_api_key"
  }
}
```

Эндпоинт Anthropic (`/v1/messages`) помещает те же поля в обёртку Anthropic:

```json
{
  "type": "error",
  "error": {
    "message": "🧀 Invalid or disabled OpenCheese API key.",
    "type": "authentication_error",
    "code": "invalid_api_key"
  }
}
```

Если после ошибки имеет смысл подождать, в теле есть поле `retry_after` (в секундах), а в ответе —
заголовок `Retry-After`.

## Ошибки после начала потока

Когда потоковый ответ начался, HTTP-статус уже `200`, поэтому сбой приходит в самом потоке:

- `/v1/chat/completions`: сообщение приходит как текст ассистента, затем — завершающий фрагмент и
  `data: [DONE]`.
- `/v1/responses`: событие `response.failed` с полями `response.error.code` и
  `response.error.message`.
- `/v1/messages`: событие `error` с объектом ошибки Anthropic.

## Chat Completions возвращает некоторые ошибки как ответ ассистента

На `/v1/chat/completions` проблемы, которые требуют ваших действий, приходят как сообщение
ассистента в ответе `200`, переданном потоком `text/event-stream`, даже если `stream` равен
`false`:

```text
data: {"object":"chat.completion.chunk","model":"opencheese","choices":[{"index":0,"delta":{"role":"assistant","content":"🧀 Invalid or disabled OpenCheese API key."},"finish_reason":"stop"}]}

data: [DONE]
```

Так сообщается о недействительном или отключённом ключе, заблокированном аккаунте, недопустимом
заголовке сессии, теле, которое не является корректным запросом, запросе, заблокированном политикой
контента, и о любом лимите баланса или тарифа. Благодаря этому чат-приложения показывают
сообщение вам. В собственном коде проверяйте `Content-Type`: `text/event-stream` в ответе на
непотоковый запрос означает, что пришло именно такое сообщение. Все остальные ошибки этого эндпоинта
приходят с обычным статусом и телом в JSON.

## Коды

| Код | Статус | Что произошло | Повторять? |
| --- | --- | --- | --- |
| `invalid_api_key` | 401 | Ключ неверный, отозван или отключён. | Нет |
| `account_banned`, `account_frozen`, `account_disabled` | 403 | Аккаунт не может отправлять запросы; причина указана в панели. | Нет |
| `invalid_request_error` | 400 | Тело не является корректным запросом для этого эндпоинта. | Нет |
| `invalid_session_header` | 400 | `opencheese-session-uuid` — недопустимый идентификатор; см. [Сессии](https://opencheese.dev/ru/docs/sessions.md). | Нет |
| `model_not_found` | 404 | Модели с таким ID нет. Проверьте список [`/v1/models`](https://opencheese.dev/ru/docs/models.md). | Нет |
| `model_access_denied` | 403 | Модель отключена на вашей странице **Цены** или не входит в список моделей ключа. | Нет |
| `model_disabled`, `model_pricing_disabled` | 400 | Модель сейчас не предоставляется. | Нет |
| `unsupported_model` | 400 | Неподходящий эндпоинт для модели, например текстовая модель на `/v1/images/generations`. | Нет |
| `streaming_unsupported` | 400 | Модель не поддерживает потоковый ответ; отправьте запрос без `stream`. | Нет |
| `unsupported_reasoning_effort` | 400 | У модели нет такого уровня рассуждений; её уровни перечислены в `supported_reasoning_levels`. | Нет |
| `context_length_exceeded` | 400 | Промпт длиннее контекстного окна модели. | Нет |
| `request_blocked_by_guard` | 403 | Запрос заблокирован политикой контента. | Нет |
| `provider_refusal` | 400 | Модель отказалась отвечать. | Только с изменённым запросом |
| `moderation_blocked`, `moderation_blocked_billed` | 400 | Политика контента модели отклонила промпт или изображение. | Только с изменённым запросом |
| `plan_limit_reached` | 429 | Исчерпан баланс, окно тарифа, лимит расходов ключа или месячный лимит либо модель не входит в ваш тариф. | После сброса лимита или пополнения баланса |
| `rate_limit_exceeded` | 429 | Слишком много запросов; см. ниже. | После `Retry-After` |
| `requested_effort_unavailable` | 429 | Уровень рассуждений временно недоступен; в ошибке перечислены другие уровни. | После `Retry-After` или с другим уровнем |
| `provider_unavailable` | 429 | Модель временно недоступна. | После `Retry-After` |
| `provider_overloaded` | 5xx | Модель перегружена. | Да, с нарастающей паузой, или с другой моделью |
| `request_timeout` | 504 | Ответ занял слишком много времени. | Да |
| `service_unavailable` | 503 | Шлюз ненадолго занят. | Да, чуть позже |

На `/v1/chat/completions` ошибки, которые приходят в виде ответа ассистента (см. выше), имеют статус
`200` вместо указанного здесь.

На `/v1/messages` вместе с `plan_limit_reached` приходит и `x-should-retry: false`, поэтому SDK
Anthropic не отправляют такой запрос повторно.

## Уровень рассуждений, которого нет у модели

```json
{
  "error": {
    "message": "🧀 Example Model is not available for Ultra effort. Choose one of these efforts: low, medium, high.",
    "type": "invalid_request_error",
    "code": "unsupported_reasoning_effort",
    "supported_reasoning_levels": ["low", "medium", "high"]
  }
}
```

Уровни идут от самого слабого к самому сильному и совпадают с теми, что `/v1/models` указывает для
этой модели. Модель без уровней отвечает, что не принимает настройку уровня рассуждений: уберите это
поле. Если вы вообще не передаёте уровень, используется уровень модели по умолчанию, и эта ошибка
не возникает.

## Лимиты запросов

У каждого аккаунта есть лимиты запросов в минуту, час, день и месяц. На запрос сверх лимита
приходит ответ `429 rate_limit_exceeded` с такими заголовками:

| Заголовок | Значение |
| --- | --- |
| `Retry-After` | Через сколько секунд можно повторить попытку. |
| `x-ratelimit-limit` | Лимит, которого вы достигли. |
| `x-ratelimit-remaining` | `0`. |
| `x-ratelimit-reset` | Время сброса окна в формате ISO 8601. |

На очень короткий всплеск запросов тоже может прийти `429`, но без этих заголовков: подождите
несколько секунд, прежде чем повторить запрос. Эндпоинты аккаунта (`/v1/key`, `/v1/plan`,
`/v1/balance`) и `/v1/messages/count_tokens` в лимитах не учитываются.

## Повторные попытки

- Повторяйте запросы после `429`, `5xx` и `request_timeout`: соблюдайте `Retry-After`, если он
  есть, а иначе увеличивайте паузу экспоненциально.
- Не повторяйте без изменений запросы после `400`, `401`, `403` и `404`: тот же запрос завершится
  той же ошибкой.
- `plan_limit_reached` снимается, когда окно сбрасывается, когда вы пополняете баланс или когда
  включаете **Использовать модели за баланс** на странице **Обзор** в панели.

## Уведомления рядом с ответом

Успешный ответ может содержать `opencheese_notifications` — список уведомлений о вашем аккаунте,
например о том, что окно тарифа скоро закончится. На сам ответ они не влияют.
