简体中文 English 繁體中文 日本語 Русский
本文为静态镜像,内容以交互版为准 在交互式文档中心打开 →

Система исключений и руководство по обработке

Все пользовательские исключения ErisPulse наследуются от ErisPulseError, а исключения底层 библиотек (например, aiohttp, aiomysql и др.) перехватываются и преобразуются во внутренние исключения ErisPulse — код бизнес-логики не должен зависеть от типов исключений底层 библиотек.

{!--< tips >!--}

  1. Для широкого перехвата: перехватите ErisPulseError (базовый класс всех исключений фреймворка)
  2. Для точного обработки: перехватите по модулю (например, ModuleCallTimeoutError, StorageUnreachableError)
  3. Операции с хранилищем по умолчанию не вызывают исключения: при сбое записывается в лог и возвращается False / None / default; изменение состояния подключения подписывается на события storage.unreachable / storage.recovered {!--< /tips >!--}

Обзор исключений

ErisPulseError                      # Базовый класс всех исключений фреймворка
├── ClientError                     # Базовый класс исключений HTTP/WS клиентов (Core/client)
│   ├── ClientConnectionError       # Ошибки на уровне соединения: неудача DNS, отказ в соединении, недоступность сети
│   ├── ClientTimeoutError          # Ошибка таймаута запроса
│   └── HTTPStatusError             # Ошибки HTTP статус-кода (например, 4xx/5xx и raise_for_status)
├── WebSocketError                  # Базовый класс исключений WebSocket (WS соединения в Core/client)
│   └── WebSocketDisconnect         # Отключение WebSocket (общее для сервера и клиента)
├── StorageError                    # Базовый класс исключений хранилища (Core/storage)
│   └── StorageUnreachableError     # Недостижимость хранилища (исчерпаны попытки подключения: недоступна база данных, ошибка учетных данных)
├── InteractionError                # Базовый класс исключений взаимодействия (Core/Event/interaction)
│   ├── InteractionCancelled        # Отмена ожидания/аренды (wait_reply возвращает None)
│   └── SessionOccupiedError        # Исключение занятости сессии (hold() не удалось получить аренду)
├── ModuleError                     # Базовый класс исключений модуля (Core/module)
│   └── ModuleCallError             # Базовый класс исключений вызова модуля
│       ├── ModuleNotAvailableError # Целевой модуль не зарегистрирован/не включен/инициализация не удалась (включая ленивую загрузку)
│       ├── ServiceNotProvidedError # Целевой модуль не объявил эту службу (не в белом списке meta.services)
│       └── ModuleCallTimeoutError  # Выполнение вызываемого метода превысило таймаут (по умолчанию 30 сек.)
└── StrictModeError                 # Критическое нарушение строгого режима (прерывает процесс запуска, loaders/strict)

Структурированные атрибуты

После перехвата исключения можно получить структурированные атрибуты (без разбора текста сообщения):

Исключение Атрибуты
ClientError (включая подклассы) .url URL запроса, .method метод запроса, .attempts количество попыток (когда исчерпаны попытки повтора)
HTTPStatusError .status код статуса, .message сообщение ответа
WebSocketDisconnect .code код закрытия, .reason причина закрытия
StorageUnreachableError .backend имя хранилища (sqlite/mysql/postgres), .cooldown время охлаждения в секундах
ModuleCallError (включая подклассы) .module имя модуля, .method имя метода
ModuleCallTimeoutError Наследует .module/.method, также .timeout лимит таймаута (сек.)
InteractionCancelled .reason причина отмены, .wait_key ключ сессии
SessionOccupiedError .wait_key ключ сессии, .owner владелец
StrictModeError .violations список нарушений
from ErisPulse.Core.Bases.errors import ClientError

try:
    resp = await sdk.client.post(url, json=payload)
except ClientError as e:
    print(f"Запрос не удался {e.method} {e.url}, всего попыток {e.attempts}: {e}")

Описание исключений и места их возникновения

Клиентские исключения — Core/client.py / Core/Bases/client.py

Исключения возникают при отправке запросов через sdk.client / HTTP клиент и WebSocket клиент:

Исключение Место возникновения Типичные сценарии
ClientError Уровень обертки запросов Другие ошибки клиента (исключения底层 aiohttp уже преобразованы)
ClientConnectionError Этап установки соединения Целевой сервис недоступен, DNS неудачен, соединение отклонено
ClientTimeoutError Этап выполнения запроса Время запроса превышено
HTTPStatusError raise_for_status() Код статуса ответа 4xx/5xx
from ErisPulse.Core.Bases.errors import ClientTimeoutError

try:
    resp = await sdk.client.get("https://api.example.com", timeout=5)
except ClientTimeoutError:
    ...

WebSocket исключения — Core/client.py (методы send / receive)

Исключение Место возникновения Типичные сценарии
WebSocketError Методы отправки/приема WebSocket Соединение закрыто, получено неожиданное сообщение,底层 WebSocket исключения
WebSocketDisconnect Методы отправки/приема WebSocket Удаленный узел нормально закрыл соединение (фреймворк автоматически переподключится)

Исключения хранилища — Core/storage / Core/Bases/sql_base.py

Исключение Место возникновения Типичные сценарии
StorageError Уровень хранилища Базовый класс исключений хранилища
StorageUnreachableError Этап создания пула База данных недоступна / ошибка учетных данных / сетевая изоляция, исчерпаны попытки повтора

Поведение при сбое операций с хранилищем: операции KV и запросов по умолчанию не вызывают исключений — при сбое записывается в лог уровня ERROR и возвращается False / None / default (чтобы избежать блокировки работы фреймворка). Следовательно, код бизнес-логики обычно не перехватывает StorageUnreachableError (исключение предназначено для прямого доступа к底层 хранилищу или пользовательских backends). Для отслеживания состояния подключения во время выполнения подписывайтесь на события жизненного цикла storage.unreachable / storage.recovered (см. События жизненного цикла).

Поведение при сбое подключения см. Backends хранилища → Поведение при сбое подключения.

Исключения взаимодействия — Core/Event/interaction.py

Связанные с wait_reply / арендой сессии / таймером напоминаний:

Исключение Место возникновения Типичные сценарии
InteractionError Уровень взаимодействия Базовый класс исключений взаимодействия
InteractionCancelled При отмене ожидания Устанавливается на future ожидания (ожидающий код может перехватить и получить .reason)
SessionOccupiedError При неудачной аренде hold() Сессия уже занята другим owner (.owner содержит владельца)

При отмене ожидания (отключение модуля / завершение платформы / новое ожидание в той же сессии) wait_reply возвращает None, а не вызывает исключение (внутри InteractionCancelled преобразуется); при необходимости различить причину отмены перехватывайте это исключение напрямую.

Исключения модулей — Core/module.py (sdk.module.call)

Исключение Место возникновения Типичные сценарии
ModuleError Система модулей Базовый класс исключений модулей
ModuleCallError module.call() Базовый класс исключений вызова модуля
ModuleNotAvailableError module.call() / ленивое получение атрибута Целевой модуль не зарегистрирован / не включен / инициализация не удалась
ServiceNotProvidedError module.call() Целевой meta.services не объявил этот метод
ModuleCallTimeoutError module.call() Вызываемый корутина превысила лимит таймаута (по умолчанию 30 сек.)

"Целевой модуль недоступен" в разных путях доступа:

Путь доступа Исключение
await sdk.module.call("X", "method") ModuleNotAvailableError (типизированное)
sdk.module.X.attr (ленивое получение атрибута, после неудачной инициализации) ModuleNotAvailableError
sdk.module.X (при попытке доступа к неактивированному модулю) AttributeError (Python-семантика атрибута, hasattr зависит от этой семантики)
from ErisPulse.Core.Bases.errors import ModuleNotAvailableError, ServiceNotProvidedError

try:
    history = await sdk.module.call("Chat", "get_history", session_id, n=20)
except ModuleNotAvailableError:
    ...  # Целевой модуль не существует / не включен
except ServiceNotProvidedError:
    ...  # Целевой модуль не предоставляет эту службу

Проверка параметров фреймворка (ValueError)

Проверка параметров построителя запросов хранилища (пустой тип столбца, Insert не dict, небезопасный тип столбца и т.д.) вызывает стандартный ValueError — такие исключения относятся к ошибкам в коде на этапе разработки, и обычный бизнес-код не должен их перехватывать, а должен исправлять вызов.

Неудача стандартных действий адаптера не вызывает исключений: возвращается словарь с кодом ответа (протокольная семантика, например, retcode=10002 означает, что действие не реализовано) — это параллельный канал ошибок, отличный от канала исключений ClientError в клиентском слое; при разработке адаптера необходимо обрабатывать оба канала.

Рекомендации по перехвату

from ErisPulse.Core import ErisPulseError  # Базовый класс экспортируется из Core

try:
    ...
except ErisPulseError as e:
    ...  # Общий перехват: все пользовательские исключения фреймворка

Скрытие шума при завершении

Долгоживущие процессы при завершении (или отмене фоновых циклов) часто генерируют множество бесполезных сообщений от интерпретатора типа Task was destroyed but it is pending!. Фреймворк включает скрытие подобного шума: в 2-секундном окне одинаковые сообщения понижаются до уровня TRACE и объединяются с подсчетом, выводится только один раз, с суффиксом «(еще N одинаковых сообщений было скрыто)». Это преднамеренное уменьшение шума — появление суффикса скрытия не означает, что фреймворк "проглотил" реальные исключения, бизнес-исключения (вышеуказанные) выводятся полностью.