# Обработка ошибок

HTTP-ошибки SkyLink на уровне всей платформы — форматы ошибок авторизации и API, валидация, повторные попытки и заголовки квот.



Когда запрос завершается ошибкой, тело ответа — JSON, но **его структура зависит от того, какой уровень отклонил вызов**. Ваш клиент должен сначала ветвиться по HTTP-статусу, а затем разбирать тело по правилам ниже. Эта страница — единственный источник истины по обработке ошибок на уровне всей платформы для всех версий API (`/v2`, `/v3`, `/v3.1`) и всех эндпоинтов.

Ошибки авторизации различаются для двух каналов — <ApiProviderToggle />

| Уровень                        | Поле верхнего уровня          | Типичные статусы                  |
| ------------------------------ | ----------------------------- | --------------------------------- |
| **Уровень авторизации / квот** | `message` или `code` (строка) | `401`, `403`, `429`               |
| **SkyLink API**                | `detail` (строка или массив)  | `404`, `422`, `500`, `502`, `504` |

Всегда читайте `Content-Type: application/json`. Не рассчитывайте на один формат ошибки для всех кодов статуса.

Справочные страницы эндпоинтов описывают **только** те ошибки, смысл или текст `detail` которых специфичен для данного ресурса (например, отсутствие станции [METAR](https://en.wikipedia.org/wiki/METAR) и невыпущенный [TAF](https://en.wikipedia.org/wiki/Terminal_aerodrome_forecast)). По вопросам авторизации, валидации, ограничений частоты запросов и серверных ошибок они ссылаются на эту страницу.

***

## Ошибки авторизации и квот [#auth-and-quota-errors]

Они возникают до обращения к уровню данных или вместо него. Тело содержит &#x2A;*`message`*&#x2A; или &#x2A;*`code`**, а не `detail`.

<ForProvider only="direct">
  **401 — API-ключ отсутствует или недействителен**

  ```json
  {
    "code": "missing_api_key",
    "message": "Provide your key in the x-api-key header."
  }
  ```

  Ключ считывается только из заголовка `x-api-key` — передача его как query-параметра не принимается и возвращает тот же `401`.

  **403 — эндпоинт не входит в ваш тариф**

  ```json
  {
    "code": "plan_forbidden",
    "message": "This endpoint is not included in your plan."
  }
  ```

  **429 — достигнут потолок защиты от злоупотреблений**

  ```json
  {
    "message": "Too many requests"
  }
  ```

  См. [Ограничения частоты запросов](/docs/rate-limits#quota-headers) — в прямом API это суточный потолок защиты от злоупотреблений, а не ваш месячный лимит.
</ForProvider>

<ForProvider only="rapidapi">
  **401 — API-ключ недействителен или отсутствует**

  ```json
  {
    "message": "Invalid API key. Go to https://docs.rapidapi.com/docs/keys for more info."
  }
  ```

  **403 — нет подписки**

  ```json
  {
    "message": "You are not subscribed to this API."
  }
  ```

  **429 — ограничение частоты запросов**

  ```json
  {
    "message": "Too many requests"
  }
  ```
</ForProvider>

<DocDetails title="JSON Schema — ошибки шлюза (401, 403, 429)">
  ```json
  {
    "type": "object",
    "required": ["message"],
    "properties": {
      "message": { "type": "string" }
    },
    "additionalProperties": true
  }
  ```
</DocDetails>

Исправьте ключ или подписку — **не повторяйте** запросы с `401` или `403`. При `429` сделайте паузу и повторите запрос; см. [Правила повторных попыток](#retry-rules) и [Ограничения частоты запросов](/docs/rate-limits) о заголовках квот.

Настройка заголовков описана в разделе [Начало работы](/docs/getting-started).

***

## Ошибки валидации (422) [#validation-errors-422]

Если запрос дошёл до SkyLink, но не прошёл валидацию входных данных, `detail` — это **массив** объектов:

```json
{
  "detail": [
    {
      "type": "string_too_short",
      "loc": ["path", "icao"],
      "msg": "String should have at least 4 characters",
      "input": "KF",
      "ctx": { "min_length": 4 }
    }
  ]
}
```

| Поле             | Описание                                                                      |
| ---------------- | ----------------------------------------------------------------------------- |
| `detail[].type`  | Идентификатор валидатора, например `string_too_short`                         |
| `detail[].loc`   | Путь к ошибочному полю, например `["path", "icao"]` или `["query", "parsed"]` |
| `detail[].msg`   | Понятное человеку пояснение                                                   |
| `detail[].input` | Отклонённое значение                                                          |
| `detail[].ctx`   | Необязательные ограничения                                                    |

<DocDetails title="JSON Schema — 422 (массив detail)">
  ```json
  {
    "type": "object",
    "required": ["detail"],
    "properties": {
      "detail": {
        "type": "array",
        "items": {
          "type": "object",
          "required": ["type", "loc", "msg", "input"],
          "properties": {
            "type": { "type": "string" },
            "loc": { "type": "array", "items": { "type": "string" } },
            "msg": { "type": "string" },
            "input": { "type": "string" },
            "ctx": { "type": "object" }
          },
          "additionalProperties": true
        }
      }
    },
    "additionalProperties": false
  }
  ```
</DocDetails>

**Не повторяйте запрос** — исправьте параметры и отправьте его снова.

***

## Серверные ошибки и ошибки внешних сервисов (5xx) [#server-and-upstream-errors-5xx]

Когда происходит сбой SkyLink или внешнего сервиса данных:

```json
{
  "detail": "Internal Server Error"
}
```

<DocDetails title="JSON Schema — 5xx (строка detail)">
  Та же структура, что и у бизнес-строк `detail` (`404` тоже использует эту форму):

  ```json
  {
    "type": "object",
    "required": ["detail"],
    "properties": {
      "detail": { "type": "string" }
    },
    "additionalProperties": false
  }
  ```
</DocDetails>

| Статус | Значение                                |
| ------ | --------------------------------------- |
| `500`  | Внутренняя ошибка сервера               |
| `502`  | Внешний сервис данных недоступен        |
| `504`  | Истекло время ожидания внешнего сервиса |

**Повторяйте запрос** с экспоненциальной задержкой (не более 3 попыток). См. [Правила повторных попыток](#retry-rules).

***

## Не найдено (404) [#not-found-404]

Ответы `404` используют строковую форму `detail` SkyLink, но **сообщение и смысл зависят от эндпоинта**: [METAR](https://en.wikipedia.org/wiki/METAR) без станции, [TAF](https://en.wikipedia.org/wiki/Terminal_aerodrome_forecast) без прогноза, неизвестный ICAO-код аэропорта и отсутствующий исторический рейс имеют разную семантику. Описывайте `404` на каждой **справочной странице** (или в руководстве по интеграции раздела, если шаблон охватывает несколько эндпоинтов). Не дублируйте здесь все варианты; из своего обработчика ссылайтесь на страницу, соответствующую вызванному эндпоинту.

| Раздел                    | Где описан `404`                                                                                                                                                                                                                    |
| ------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Погода — METAR / TAF      | [METAR](/docs/v31/weather-metar#error-responses) · [TAF](/docs/v31/weather-taf#error-responses) · [Когда данные о погоде отсутствуют](/docs/v31/weather-integration-errors#when-weather-data-is-missing)                            |
| Погода — на маршруте      | [Ветер на высотах](/docs/v31/winds-aloft#error-responses) · [PIREP](/docs/v31/pireps#error-responses) · [AIRMET/SIGMET](/docs/v31/airsigmet#error-responses)                                                                        |
| Данные аэропортов         | [Аэропорты](/docs/v31/airports#error-responses) · [Поиск аэропортов](/docs/v31/airport-search#error-responses)                                                                                                                      |
| Справочные данные         | [Страны](/docs/v31/countries#error-responses) · [Регионы](/docs/v31/regions#error-responses)                                                                                                                                        |
| ADS-B в реальном времени  | [Отслеживание ADS-B](/docs/v31/adsb#error-responses)                                                                                                                                                                                |
| Исторические данные ADS-B | [Обзор исторических данных ADS-B](/docs/v31/historical-adsb#error-responses) · [Сведения о рейсе](/docs/v31/historical-adsb-flight-detail#error-responses) · [История позиций](/docs/v31/historical-adsb-positions#error-responses) |
| Карты                     | [Аэронавигационные карты](/docs/v31/charts#error-responses)                                                                                                                                                                         |
| Лётная эксплуатация       | [Статус рейса](/docs/v31/flight-status#error-responses)                                                                                                                                                                             |
| Вебхуки                   | [Подписки](/docs/v31/webhooks-subscriptions#error-responses)                                                                                                                                                                        |

**Важно:** некоторые эндпоинты вместо `404` возвращают пустой результат с HTTP `200` (например, [поиск воздушного судна](/docs/v31/aircraft-lookup), когда ни один регистрационный номер не совпал). Прежде чем сводить все случаи «не найдено» к `404`, сверьтесь со справочной страницей.

Ошибки `401`/`403`, связанные с ограничениями тарифа для исторических данных [ADS-B](https://www.faa.gov/air_traffic/technology/adsb) (подписка ниже уровня, необходимого для префикса пути), описаны на странице [Обзор исторических данных ADS-B](/docs/v31/historical-adsb#error-responses), а не здесь.

***

## Правила повторных попыток [#retry-rules]

| Статус              | Повторять? | Действие                                                                               |
| ------------------- | ---------- | -------------------------------------------------------------------------------------- |
| `401`, `403`        | Нет        | Исправьте [API-ключ / подписку](/docs/getting-started#key-security)                    |
| `404`               | Нет        | Резервный сценарий для конкретного эндпоинта — см. справочную страницу                 |
| `422`               | Нет        | Исправьте входные данные                                                               |
| `429`               | Да         | Экспоненциальная задержка; проверьте [заголовки квот](/docs/rate-limits#quota-headers) |
| `500`, `502`, `504` | Да         | Экспоненциальная задержка (не более 3 попыток)                                         |

```python
import time
import requests

RETRYABLE = {429, 500, 502, 504}


def fetch_with_retry(url: str, headers: dict, params: dict | None = None, max_retries: int = 3) -> requests.Response:
    delay = 1.0
    for attempt in range(max_retries + 1):
        r = requests.get(url, headers=headers, params=params, timeout=(10, 15))
        if r.status_code not in RETRYABLE or attempt == max_retries:
            return r
        time.sleep(delay)
        delay *= 2
    return r
```

О квотах тарифов, стоимости превышения квоты и уровнях путей исторических данных [ADS-B](https://www.faa.gov/air_traffic/technology/adsb) см. [Ограничения частоты запросов](/docs/rate-limits).
