# Ошибки и лимиты

Формат ошибок, HTTP-коды, коды ошибок по категориям, заголовки лимитов и retry-логика.

## Ошибки

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

REST-эндпоинты возвращают ошибки в едином формате с полями `code`, `message` и необязательным `hint`, обёрнутыми в объект `detail`. Инструменты MCP используют другой формат — см. ниже.

```json
{
  "detail": {
    "code": "DOCUMENT_FORMAT_UNSUPPORTED",
    "message": "The uploaded file format is not supported.",
    "hint": "Supported formats: PDF, DOCX, XLSX, TXT, CSV, JSON, HTML, MD"
  }
}
```

| Поле | Тип | Описание |
| --- | --- | --- |
| code | string | Машиночитаемый код ошибки (например, DOCUMENT_FORMAT_UNSUPPORTED) |
| message | string | Человекочитаемое описание ошибки |
| hint | string? | Необязательная подсказка для исправления ошибки |

### Ошибки инструментов MCP

Инструменты, вызываемые через MCP (например, `connector_execute`), не используют HTTP-коды. Они возвращают структурированный конверт с флагом `retryable` вместо `hint`:

```json
{
  "code": "SERVICE_RATE_LIMITED",
  "message": "Upstream service is rate limiting requests.",
  "retryable": true
}
```

Если `retryable` равно `true`, вызов можно повторить после небольшой задержки; если `false`, повтор с теми же аргументами снова завершится ошибкой.

### HTTP-коды ответов

| Код | Описание |
| --- | --- |
| 200 | Success |
| 201 | Created — ресурс успешно создан |
| 204 | No Content — успешное удаление |
| 400 | Bad Request — неверные параметры или тело запроса |
| 401 | Unauthorized — отсутствует или недействителен токен |
| 403 | Forbidden — у токена нет нужного scope |
| 404 | Not Found — ресурс не существует |
| 409 | Conflict — ресурс уже существует (идемпотентность) |
| 422 | Unprocessable Entity — ошибка валидации |
| 429 | Too Many Requests — превышен лимит запросов |
| 500 | Internal Server Error |
| 504 | Gateway Timeout — запрос выполнялся слишком долго |

### Коды ошибок по категориям

Часто встречающиеся коды ошибок по категориям. Полный актуальный список доступен в [интерактивном API-эксплорере](/docs/api-explorer).

| Категория | Коды |
| --- | --- |
| Authentication | `AUTH_TOKEN_EXPIRED` `AUTH_TOKEN_INVALID` `AUTH_HEADER_MISSING` `AUTH_PERMISSION_DENIED` `AUTH_SCOPE_MISSING` |
| Documents | `DOCUMENT_FORMAT_UNSUPPORTED` `DOCUMENT_SIZE_EXCEEDED` `DOCUMENT_PASSWORD_PROTECTED` `DOCUMENT_OCR_FAILED` `DOCUMENT_NOT_FOUND` |
| LLM | `LLM_PROVIDER_UNAVAILABLE` `LLM_REQUEST_TIMEOUT` `LLM_PROVIDER_ERROR` `LLM_RATE_LIMITED` |
| Tools | `TOOL_VALIDATION_FAILED` `TOOL_NOT_FOUND` `TOOL_TIMEOUT` `TOOL_EXECUTION_FAILED` |
| Data | `DATA_NOT_FOUND` `DATA_CONFLICT` `DATA_INVALID_PARAMETER` `DATA_QUOTA_EXCEEDED` |
| Service | `SERVICE_UNAVAILABLE` `SERVICE_RATE_LIMITED` `SERVICE_CONNECTION_LOST` `SERVICE_CIRCUIT_OPEN` |

### Пример обработки ошибок

Рекомендуемый подход к обработке ошибок API на Python.

```python
import requests

response = requests.post(url, headers=headers, json=body)

if response.status_code >= 400:
    error = response.json().get("detail", {})
    code = error.get("code", "UNKNOWN")
    message = error.get("message", "An error occurred")
    hint = error.get("hint")

    if response.status_code == 429:
        # Rate limited — retry after delay
        retry_after = response.headers.get("Retry-After", "60")
        print(f"Rate limited. Retry after {retry_after}s")
    elif response.status_code == 401:
        # Authentication error
        print(f"Auth error: {message}")
    else:
        print(f"Error [{code}]: {message}")
        if hint:
            print(f"Hint: {hint}")
```

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

### Заголовки лимитов

Каждый ответ API включает заголовки с информацией о текущих лимитах.

```bash
HTTP/1.1 200 OK
X-RateLimit-Limit: 60
X-RateLimit-Remaining: 58
X-RateLimit-Reset: 1711540800
```

| Заголовок | Описание |
| --- | --- |
| X-RateLimit-Limit | Максимальное количество запросов в окне |
| X-RateLimit-Remaining | Оставшееся количество запросов |
| X-RateLimit-Reset | Unix-время сброса окна лимита |
| Retry-After | Секунды до повторной попытки (только при 429) |

### Лимиты по тарифам

Лимиты запросов в минуту зависят от вашего тарифного плана и общие для всей организации.

| Тариф | Запросы |
| --- | --- |
| Free | 10 req/min |
| Pro | 30 req/min |
| Max | 60 req/min |

Сейчас применяется только лимит в минуту.

### Ответ при превышении лимита

При превышении лимита API возвращает статус 429 с информацией о времени ожидания.

```json
{
  "detail": {
    "code": "SERVICE_RATE_LIMITED",
    "message": "Rate limit exceeded. Please retry after the reset time.",
    "hint": "Check X-RateLimit-Reset header for the reset timestamp."
  }
}
```

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

Реализуйте экспоненциальную задержку при получении 429-ответов.

```python
import time
import requests

def request_with_retry(url, headers, max_retries=3):
    for attempt in range(max_retries):
        response = requests.get(url, headers=headers)

        if response.status_code == 429:
            retry_after = int(response.headers.get("Retry-After", "60"))
            print(f"Rate limited. Retrying in {retry_after}s...")
            time.sleep(retry_after)
            continue

        return response

    raise Exception("Max retries exceeded")
```

### Лучшие практики

- Кэшируйте ответы, которые не меняются часто (например, список моделей)
- Используйте экспоненциальную задержку при повторных попытках
- Следите за заголовками X-RateLimit-Remaining для предупреждения о приближении к лимиту
- Для пакетных операций используйте batch-эндпоинты вместо множественных одиночных запросов
