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

Обработка ошибок данных о воздушных судах и ADS-B в продакшне: отсутствующие регистрационные номера, пустые фильтры и повторные попытки.



Кэширование и мониторинг: [Кэширование и мониторинг](/docs/v31/aircraft-adsb-integration-production).

**Ошибки платформы** (401, 403, 422, 429, 5xx, повторные попытки и экспоненциальная задержка, заголовки ограничения частоты запросов): [Обработка ошибок](/docs/errors).

### Поиск воздушного судна: `found` — это не 404 [#aircraft-lookup-found-is-not-a-404]

Эндпоинт регистрационных номеров всегда возвращает `200`, если сам запрос корректен, — даже если бортового номера нет в реестре воздушных судов SkyLink. Не считайте отсутствие воздушного судна HTTP-ошибкой.

| Ситуация                         | HTTP-статус | Как определить   |
| -------------------------------- | ----------- | ---------------- |
| Регистрационный номер известен   | `200`       | `found: true`    |
| Регистрационный номер неизвестен | `200`       | `found: false`   |
| Некорректный сегмент пути        | `404`       | Код HTTP-статуса |

Всегда проверяйте `found`, прежде чем читать поля `aircraft.*`. Чтение `aircraft.icao_type` в ответе с `found: false` вызовет `KeyError`.

### Лётно-технические характеристики ВС: 422 для неизвестного типа [#aircraft-performance-422-on-unknown-type]

`GET /aircraft/performance/{icao_type}` возвращает `422`, если указатель типа ICAO не распознан. Это обычная ситуация, когда вы делаете запрос по цепочке после поиска в реестре: не для каждого значения `icao_type` в реестре есть соответствующая запись о характеристиках.

| Ответ | Значение                                                                                 |
| ----- | ---------------------------------------------------------------------------------------- |
| `200` | Данные о характеристиках возвращены                                                      |
| `422` | Указателя типа нет в базе данных характеристик — корректно деградируйте функциональность |

### ADS-B в реальном времени: пустой `aircraft[]` — это не ошибка [#live-ads-b-empty-aircraft-is-not-an-error]

`GET /adsb/aircraft` возвращает `200` с `aircraft: []`, если ни одно воздушное судно не соответствует заданному фильтру (радиус, bbox или позывной). Это корректный пустой результат — не показывайте баннер с ошибкой. Поток данных работает в реальном времени: воздушные суда входят в зону вашего фильтра и покидают её за считаные секунды.

Кроме того, **никогда не кэшируйте ответы [ADS-B](https://www.faa.gov/air_traffic/technology/adsb) в реальном времени**. Поток обновляется каждые несколько секунд; закэшированное местоположение устаревает к моменту следующей отрисовки.

### Исторические данные ADS-B: ограничение по тарифу и ошибки окна [#historical-ads-b-plan-gating-and-window-errors]

Эндпоинты `/ultra/history/...` требуют тарифа Pro или выше. Ответ `401` или `403` содержит ключ `message` с объяснением ограничения — показывайте это сообщение, а не общую строку «доступ запрещён».

Эндпоинт `/ultra/history/airport/{icao}/traffic` требует правильного значения `direction`:

| Правильно       | Неправильно            |
| --------------- | ---------------------- |
| `direction=arr` | `direction=arrivals`   |
| `direction=dep` | `direction=departures` |

Эндпоинт поиска `/ultra/history/flights` возвращает `422`, если запрошенное временное окно превышает максимальный срок хранения для данного пути (90 дней на `/ultra/...`). Сузьте окно и повторите запрос.

```python
import os
import requests

HEADERS = {
    "X-RapidAPI-Key": os.getenv("RAPIDAPI_KEY", "YOUR_RAPIDAPI_KEY"),
    "X-RapidAPI-Host": "skylink-api.p.rapidapi.com",
}
BASE = "https://skylink-api.p.rapidapi.com"


def lookup_registration(registration: str) -> dict | None:
    """
    Returns aircraft dict or None when not found.
    Always checks the `found` field — never treats found=false as an error.
    """
    r = requests.get(
        f"{BASE}/aircraft/registration/{registration}",
        headers=HEADERS,
        timeout=(10, 15),
    )
    r.raise_for_status()
    data = r.json()
    if not data.get("found"):
        print(f"Registration {registration} not in registry (found=false).")
        return None
    return data["aircraft"]


def fetch_performance(icao_type: str) -> dict | None:
    """
    Returns performance data or None on 422 (type not in database).
    """
    r = requests.get(
        f"{BASE}/aircraft/performance/{icao_type}",
        headers=HEADERS,
        timeout=(10, 15),
    )
    if r.status_code == 422:
        print(f"Performance data not available for type {icao_type!r} (422).")
        return None
    r.raise_for_status()
    return r.json()


def fetch_history_with_plan_gate(registration: str) -> list[dict] | None:
    """
    Fetches recent historical flights. Surfaces the plan gate message on 401/403.
    """
    r = requests.get(
        f"{BASE}/ultra/history/flights",
        headers=HEADERS,
        params={"registration": registration, "limit": 5},
        timeout=(10, 25),
    )
    if r.status_code in (401, 403):
        msg = r.json().get("message", "Plan not entitled.")
        print(f"Access denied: {msg}")
        print("Upgrade to Pro or above for /ultra/history/... endpoints.")
        return None
    r.raise_for_status()
    return r.json().get("flights", [])


def main() -> None:
    # 1. Registration lookup — check found field
    ac = lookup_registration("N636JB")
    if ac:
        print(f"Found: {ac['registration']} / {ac['icao_type']} / {ac['owner_operator']}")
        # 2. Chain to performance — handle 422
        perf = fetch_performance(ac["icao_type"])
        if perf:
            print(f"Cruise speed: {perf.get('cruise_speed_ktas')} kt")
        else:
            print("Showing registration data only — no performance record.")
    else:
        print("Show empty state to user — aircraft not in registry.")

    # 3. Historical search — handle 401/403 plan gate
    flights = fetch_history_with_plan_gate("N636JB")
    if flights is not None:
        print(f"Last {len(flights)} archived flights retrieved.")


if __name__ == "__main__":
    main()
```

Для ответов `429` и `5xx` используйте вспомогательную функцию повторных попыток со страницы [Обработка ошибок](/docs/errors#retry-rules).

***

См. также: [Поиск воздушных судов](/docs/v31/aircraft-lookup) · [Лётно-технические характеристики ВС](/docs/v31/aircraft-performance) · [ADS-B](/docs/v31/adsb) · [Исторические данные ADS-B](/docs/v31/historical-adsb) · [Паттерны для продакшна](/docs/v31/aircraft-adsb-integration-production)
