REPOSITORY / ScuroNeko/Laniakea
Wiki
Document error visibility model
Mark rc.13 priority-one backlog items done Align backlog and error docs in EN and RU
@@ -18,6 +18,10 @@ English version: [[Error-Handling]]
|
|||||||
- отправляет этот текст пользователю;
|
- отправляет этот текст пользователю;
|
||||||
- логирует исходную ошибку через текущий logger.
|
- логирует исходную ошибку через текущий logger.
|
||||||
|
|
||||||
|
По умолчанию возвращённые ошибки остаются пользовательскими ради обратной совместимости. Но Laniakea теперь даёт и явную маркировку:
|
||||||
|
- `AsUserError(err)` явно оставляет ошибку на пользовательском пути;
|
||||||
|
- `AsInternalError(err)` логирует ошибку, но подавляет автоматический ответ пользователю.
|
||||||
|
|
||||||
## Как это выглядит
|
## Как это выглядит
|
||||||
|
|
||||||
```go
|
```go
|
||||||
@@ -42,6 +46,12 @@ if err != nil {
|
|||||||
- ошибка действительно exceptional;
|
- ошибка действительно exceptional;
|
||||||
- хочешь централизованное логирование и formatting.
|
- хочешь централизованное логирование и formatting.
|
||||||
|
|
||||||
|
Если хочешь сделать намерение явным, можно вернуть:
|
||||||
|
|
||||||
|
```go
|
||||||
|
return laniakea.AsUserError(errors.New("access denied"))
|
||||||
|
```
|
||||||
|
|
||||||
## Когда лучше ответить вручную
|
## Когда лучше ответить вручную
|
||||||
|
|
||||||
Лучше ответить вручную и вернуть `nil`, когда:
|
Лучше ответить вручную и вернуть `nil`, когда:
|
||||||
@@ -58,6 +68,21 @@ if !allowed {
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## Когда возвращать internal-only ошибку
|
||||||
|
|
||||||
|
Возвращай `AsInternalError(...)`, когда:
|
||||||
|
- ошибку нужно видеть в логах, но не нужно показывать пользователю;
|
||||||
|
- это внутренний сбой инфраструктуры или инварианта;
|
||||||
|
- сырой текст ошибки даст плохой или шумный UX.
|
||||||
|
|
||||||
|
Пример:
|
||||||
|
|
||||||
|
```go
|
||||||
|
if err := db.WarmCache(); err != nil {
|
||||||
|
return laniakea.AsInternalError(fmt.Errorf("warm cache: %w", err))
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
## Callback-specific поведение
|
## Callback-specific поведение
|
||||||
|
|
||||||
Для callback-потока возвращённая ошибка превращается не в обычное сообщение в чат, а в ответ на callback query.
|
Для callback-потока возвращённая ошибка превращается не в обычное сообщение в чат, а в ответ на callback query.
|
||||||
@@ -84,6 +109,8 @@ Middleware не возвращает `error`, он возвращает `bool`.
|
|||||||
- он сам показывает ответ;
|
- он сам показывает ответ;
|
||||||
- возвращает `false`.
|
- возвращает `false`.
|
||||||
|
|
||||||
|
Internal-only ошибки идут по тому же logger path, но не создают автоматический ответ пользователю.
|
||||||
|
|
||||||
## Что читать дальше
|
## Что читать дальше
|
||||||
|
|
||||||
- [[FAQ-RU]]
|
- [[FAQ-RU]]
|
||||||
|
|||||||
@@ -24,6 +24,10 @@ func ping(ctx *laniakea.MsgContext, db *App) error {
|
|||||||
|
|
||||||
When a handler returns an error, the bot routes it through the context error helper instead of ignoring it.
|
When a handler returns an error, the bot routes it through the context error helper instead of ignoring it.
|
||||||
|
|
||||||
|
By default, returned handler errors remain user-visible for backward compatibility. Laniakea also provides explicit markers when you want to distinguish between user-facing and internal-only failures:
|
||||||
|
- `AsUserError(err)` keeps the error on the user-facing path explicitly;
|
||||||
|
- `AsInternalError(err)` logs the error but suppresses the automatic user reply.
|
||||||
|
|
||||||
## What happens when an error is returned
|
## What happens when an error is returned
|
||||||
|
|
||||||
The context error flow does three things:
|
The context error flow does three things:
|
||||||
@@ -66,6 +70,12 @@ if !allowed {
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
If you want to make the intent explicit, you can return:
|
||||||
|
|
||||||
|
```go
|
||||||
|
return laniakea.AsUserError(errors.New("access denied"))
|
||||||
|
```
|
||||||
|
|
||||||
### Reply manually and return `nil`
|
### Reply manually and return `nil`
|
||||||
|
|
||||||
Use this when:
|
Use this when:
|
||||||
@@ -80,6 +90,19 @@ if !allowed {
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### Return an internal-only error
|
||||||
|
|
||||||
|
Use this when:
|
||||||
|
- the failure should be logged for operators, not shown to the user;
|
||||||
|
- the error indicates an internal invariant problem or infrastructure issue;
|
||||||
|
- exposing the raw error text would be noisy or misleading UX.
|
||||||
|
|
||||||
|
```go
|
||||||
|
if err := db.WarmCache(); err != nil {
|
||||||
|
return laniakea.AsInternalError(fmt.Errorf("warm cache: %w", err))
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
## Good places to return errors
|
## Good places to return errors
|
||||||
|
|
||||||
Returning an error is a good fit for:
|
Returning an error is a good fit for:
|
||||||
@@ -150,6 +173,8 @@ Depending on the route, that may be:
|
|||||||
|
|
||||||
This is one reason returning errors is useful: it keeps operational visibility aligned with the same handler path that produced the user-facing failure.
|
This is one reason returning errors is useful: it keeps operational visibility aligned with the same handler path that produced the user-facing failure.
|
||||||
|
|
||||||
|
Internal-only errors follow the same logging path, but they do not produce the automatic user reply.
|
||||||
|
|
||||||
Related page:
|
Related page:
|
||||||
- [[Logging]]
|
- [[Logging]]
|
||||||
|
|
||||||
@@ -195,6 +220,7 @@ plugin.NewPayload(func(ctx *laniakea.MsgContext, db *App) error {
|
|||||||
## Recommendations
|
## Recommendations
|
||||||
|
|
||||||
- Return `error` when you want centralized error formatting and logging.
|
- Return `error` when you want centralized error formatting and logging.
|
||||||
|
- Wrap with `AsInternalError(...)` when the centralized path should log but stay silent for the user.
|
||||||
- Reply manually and return `nil` when the response is part of normal control flow.
|
- Reply manually and return `nil` when the response is part of normal control flow.
|
||||||
- Keep user-facing error strings clear and action-oriented.
|
- Keep user-facing error strings clear and action-oriented.
|
||||||
- Use one consistent style across a plugin when possible.
|
- Use one consistent style across a plugin when possible.
|
||||||
|
|||||||
+71
-7
@@ -4,23 +4,87 @@
|
|||||||
|
|
||||||
## Приоритет 1 — Срочно
|
## Приоритет 1 — Срочно
|
||||||
|
|
||||||
- Контракт схемы обновлений: обработка обновлений уже есть, но нет формального понятия уровня фреймворка, описывающего, какие поля `MsgContext` гарантированы для каких видов обновлений.
|
|
||||||
- Модель пользовательских и внутренних ошибок: у фреймворка есть единый поток обработки ошибок, но он ещё плохо различает пользовательские, внутренние, повторяемые и тихие ошибки.
|
|
||||||
- Модель фиксации конфигурации: у фреймворка уже есть реальные точки фиксации вроде `AddPlugins(...)`, но пока это скорее факт реализации, чем явная верхнеуровневая концепция.
|
|
||||||
|
|
||||||
## Приоритет 2 — Важно
|
|
||||||
|
|
||||||
- Модель выполнения webhook: у библиотеки есть хорошая polling-модель, но нет полноценной модели выполнения webhook на уровне фреймворка.
|
- Модель выполнения webhook: у библиотеки есть хорошая polling-модель, но нет полноценной модели выполнения webhook на уровне фреймворка.
|
||||||
- Модель авторизации и политик: middleware могут реализовать аутентификацию и права доступа, но нет явной модели уровня фреймворка для политик доступа, ролей или проверок возможностей.
|
- Модель авторизации и политик: middleware могут реализовать аутентификацию и права доступа, но нет явной модели уровня фреймворка для политик доступа, ролей или проверок возможностей.
|
||||||
- Модель наблюдаемости: логирование уже сильное, но метрики, трассировка и структурированные хуки фреймворка пока не являются полноценной частью API.
|
- Модель наблюдаемости: логирование уже сильное, но метрики, трассировка и структурированные хуки фреймворка пока не являются полноценной частью API.
|
||||||
|
|
||||||
## Приоритет 3 — Стратегически
|
## Приоритет 2 — Важно
|
||||||
|
|
||||||
- Модель сервисного слоя и графа зависимостей: `DatabaseContext(T)` намеренно минималистичен, но нет более сильной концепции уровня фреймворка для сервисов приложения или зависимостей с ограниченной областью действия.
|
- Модель сервисного слоя и графа зависимостей: `DatabaseContext(T)` намеренно минималистичен, но нет более сильной концепции уровня фреймворка для сервисов приложения или зависимостей с ограниченной областью действия.
|
||||||
- Контракт композиции плагинов: плагины — хороший способ группировки, но нет явной модели зависимостей плагинов, общих возможностей или контрактов композиции.
|
- Контракт композиции плагинов: плагины — хороший способ группировки, но нет явной модели зависимостей плагинов, общих возможностей или контрактов композиции.
|
||||||
|
|
||||||
## Done
|
## Done
|
||||||
|
|
||||||
|
### [1.0.0-rc.13] Модель пользовательских и внутренних ошибок
|
||||||
|
|
||||||
|
Текущее состояние:
|
||||||
|
|
||||||
|
- У фреймворка по-прежнему один централизованный путь ошибок из обработчиков, но теперь он поддерживает явное разделение на пользовательские и внутренние ошибки.
|
||||||
|
- Неразмеченные возвращённые ошибки остаются пользовательскими ради обратной совместимости.
|
||||||
|
- Внутренние сбои теперь можно логировать без автоматической отправки их сырого текста пользователю.
|
||||||
|
|
||||||
|
Почему это важно:
|
||||||
|
|
||||||
|
- Единый поток ошибок полезен, но не каждая ошибка должна превращаться в ответ пользователю.
|
||||||
|
- Сбои инфраструктуры, нарушения внутренних инвариантов и ошибки маршрутизации часто важны для операторов, но только засоряют или портят пользовательский UX.
|
||||||
|
- Без такой классификации пользователю либо начинают утекать внутренние ошибки, либо приходится полностью обходить централизованный путь.
|
||||||
|
|
||||||
|
Что теперь есть:
|
||||||
|
|
||||||
|
- `AsUserError(...)` и `AsInternalError(...)` для явной классификации возвращаемых ошибок.
|
||||||
|
- `IsUserError(...)` и `IsInternalError(...)` для проверки этой классификации на стороне фреймворка.
|
||||||
|
- Обновлённое поведение `MsgContext.Error(...)`: все ошибки по-прежнему логируются, но для внутренних ошибок автоматический ответ пользователю подавляется.
|
||||||
|
- Регрессионные тесты для message и callback потоков.
|
||||||
|
|
||||||
|
Практическая цель:
|
||||||
|
|
||||||
|
- Сохранить централизованный error flow, но сделать пользовательские и операторские ошибки действительно разными по поведению.
|
||||||
|
|
||||||
|
### [1.0.0-rc.13] Модель фиксации конфигурации
|
||||||
|
|
||||||
|
Текущее состояние:
|
||||||
|
|
||||||
|
- Фреймворк теперь считает конфигурацию бота структурно завершённой после начала первого `Run()` или `RunWithContext(...)`.
|
||||||
|
- Поздние bot-level попытки мутации больше не применяются частично после старта runtime.
|
||||||
|
- Границы между регистрацией плагинов, стартом runtime и фиксацией конфигурации теперь оформлены как явное поведение фреймворка и закреплены тестами.
|
||||||
|
|
||||||
|
Почему это важно:
|
||||||
|
|
||||||
|
- Runtime-мутация prefixes, middleware, payload policy, локализации, runners или общих зависимостей слишком трудно поддаётся безопасному пониманию.
|
||||||
|
- У фреймворка уже были реальные точки фиксации вроде `AddPlugins(...)`; теперь они оформлены как явный контракт, а не как скрытая деталь реализации.
|
||||||
|
|
||||||
|
Что теперь есть:
|
||||||
|
|
||||||
|
- Bot-level конфигурационные методы игнорируют поздние вызовы после старта runtime.
|
||||||
|
- Правила жизненного цикла и freeze-модели задокументированы как полноценная концепция.
|
||||||
|
- Регрессионные тесты закрепляют игнорирование поздних мутаций.
|
||||||
|
|
||||||
|
Практическая цель:
|
||||||
|
|
||||||
|
- Сделать старт, runtime и владение конфигурацией достаточно предсказуемыми для стабильного контракта фреймворка.
|
||||||
|
|
||||||
|
### [1.0.0-rc.13] Контракт схемы обновлений
|
||||||
|
|
||||||
|
Текущее состояние:
|
||||||
|
|
||||||
|
- Нормализация обновлений уже существовала, но теперь она описана и протестирована как явный контракт уровня фреймворка.
|
||||||
|
- Категории маршрутизации и гарантии заполнения `MsgContext` теперь рассматриваются как полноценная часть публичной модели.
|
||||||
|
|
||||||
|
Почему это важно:
|
||||||
|
|
||||||
|
- Код обработчиков должен понимать, на какие поля `MsgContext` можно безопасно опираться в каждом потоке обновлений.
|
||||||
|
- Без явного контракта обработка обновлений остаётся понятной только через чтение реализации.
|
||||||
|
|
||||||
|
Что теперь есть:
|
||||||
|
|
||||||
|
- Задокументированная модель маршрутизации для command flow, payload flow и generic update handlers.
|
||||||
|
- Явные комментарии на полях `MsgContext`, описывающие гарантии для update-backed, callback-backed и message-backed контекстов.
|
||||||
|
- Table-driven регрессионные тесты для нормализованного update contract, включая callback target semantics и non-command update flows.
|
||||||
|
|
||||||
|
Практическая цель:
|
||||||
|
|
||||||
|
- Сделать маршрутизацию обновлений и гарантии `MsgContext` достаточно явными, чтобы на них можно было опираться как на стабильный контракт `1.0`.
|
||||||
|
|
||||||
### [1.0.0-rc.12] Conversation / Scene Model
|
### [1.0.0-rc.12] Conversation / Scene Model
|
||||||
|
|
||||||
Текущее состояние:
|
Текущее состояние:
|
||||||
|
|||||||
+71
-7
@@ -4,23 +4,87 @@ This page tracks framework-level backlog items that are about missing concepts i
|
|||||||
|
|
||||||
## Priority 1 — Urgent
|
## Priority 1 — Urgent
|
||||||
|
|
||||||
- Update schema contract: update handling exists, but there is no formal framework-level concept describing which `MsgContext` fields are guaranteed in which update kinds.
|
|
||||||
- User-facing vs internal error model: the framework has a unified error flow, but it does not yet distinguish well between user-visible, internal-only, retryable, or silent errors.
|
|
||||||
- Configuration freeze model: the framework already has real commit points like `AddPlugins(...)`, but this is still more of an implementation truth than an explicit top-level concept.
|
|
||||||
|
|
||||||
## Priority 2 — Important
|
|
||||||
|
|
||||||
- Webhook runtime model: the library has a solid polling model, but no first-class webhook execution model at the framework level.
|
- Webhook runtime model: the library has a solid polling model, but no first-class webhook execution model at the framework level.
|
||||||
- Authorization and policy model: middleware can implement auth and permissions, but there is no explicit framework concept for access policies, roles, or capability checks.
|
- Authorization and policy model: middleware can implement auth and permissions, but there is no explicit framework concept for access policies, roles, or capability checks.
|
||||||
- Observability model: logging is strong, but metrics, tracing, and structured framework hooks are still missing as first-class concepts.
|
- Observability model: logging is strong, but metrics, tracing, and structured framework hooks are still missing as first-class concepts.
|
||||||
|
|
||||||
## Priority 3 — Strategic
|
## Priority 2 — Important
|
||||||
|
|
||||||
- Service layer and dependency graph model: `DatabaseContext(T)` is intentionally minimal, but there is no stronger framework concept for application services or scoped dependencies.
|
- Service layer and dependency graph model: `DatabaseContext(T)` is intentionally minimal, but there is no stronger framework concept for application services or scoped dependencies.
|
||||||
- Plugin composition contract: plugins are a good grouping unit, but there is no explicit model for plugin dependencies, shared capabilities, or composition contracts.
|
- Plugin composition contract: plugins are a good grouping unit, but there is no explicit model for plugin dependencies, shared capabilities, or composition contracts.
|
||||||
|
|
||||||
## Done
|
## Done
|
||||||
|
|
||||||
|
### [1.0.0-rc.13] User-Facing vs Internal Error Model
|
||||||
|
|
||||||
|
Current state:
|
||||||
|
|
||||||
|
- The framework still keeps one centralized handler error path, but it now supports explicit classification between user-visible and internal-only errors.
|
||||||
|
- Unclassified returned errors remain user-visible for backward compatibility.
|
||||||
|
- Internal-only failures can now be logged without automatically sending their raw text back to the user.
|
||||||
|
|
||||||
|
Why this matters:
|
||||||
|
|
||||||
|
- A single unified error path is useful, but not every failure should become a user reply.
|
||||||
|
- Infrastructure faults, invariant violations, and internal routing failures are often important for operators while being noisy or misleading for users.
|
||||||
|
- Without classification, framework users either leak internal errors into chat UX or are forced to bypass the centralized path entirely.
|
||||||
|
|
||||||
|
What is now present:
|
||||||
|
|
||||||
|
- `AsUserError(...)` and `AsInternalError(...)` to classify returned handler errors explicitly.
|
||||||
|
- `IsUserError(...)` and `IsInternalError(...)` for framework-side inspection.
|
||||||
|
- Updated centralized `MsgContext.Error(...)` behavior that still logs all errors but suppresses the automatic user reply for internal-only failures.
|
||||||
|
- Regression coverage for both message and callback flows.
|
||||||
|
|
||||||
|
Practical target:
|
||||||
|
|
||||||
|
- Keep the centralized handler error path intact while making user-facing and operator-facing failure handling meaningfully distinct.
|
||||||
|
|
||||||
|
### [1.0.0-rc.13] Configuration Freeze Model
|
||||||
|
|
||||||
|
Current state:
|
||||||
|
|
||||||
|
- The framework now treats bot configuration as structurally complete once the first `Run()` or `RunWithContext(...)` begins.
|
||||||
|
- Late bot-level mutation attempts no longer partially apply after runtime startup.
|
||||||
|
- Plugin registration and runtime configuration boundaries are now documented and tested as explicit framework behavior.
|
||||||
|
|
||||||
|
Why this matters:
|
||||||
|
|
||||||
|
- Runtime mutation of prefixes, middleware, payload policy, localization, runners, or shared dependencies is difficult to reason about safely.
|
||||||
|
- The framework already had real commit points like `AddPlugins(...)`; this work makes those boundaries explicit instead of leaving them as implementation details.
|
||||||
|
|
||||||
|
What is now present:
|
||||||
|
|
||||||
|
- Bot-level configuration mutators now ignore late calls after runtime start.
|
||||||
|
- Lifecycle and configuration-freeze rules are documented as a first-class concept.
|
||||||
|
- Regression tests now lock down ignored late mutations.
|
||||||
|
|
||||||
|
Practical target:
|
||||||
|
|
||||||
|
- Make startup, runtime, and configuration ownership predictable enough to rely on as a stable framework contract.
|
||||||
|
|
||||||
|
### [1.0.0-rc.13] Update Schema Contract
|
||||||
|
|
||||||
|
Current state:
|
||||||
|
|
||||||
|
- Update normalization already existed, but it is now described and tested as an explicit framework-level contract.
|
||||||
|
- Routing categories and `MsgContext` population guarantees are now treated as a first-class part of the public model.
|
||||||
|
|
||||||
|
Why this matters:
|
||||||
|
|
||||||
|
- Handler code needs to know which `MsgContext` fields are safe to rely on for each update path.
|
||||||
|
- Without a formal contract, update handling remains understandable only by reading implementation details.
|
||||||
|
|
||||||
|
What is now present:
|
||||||
|
|
||||||
|
- A documented routing model for command flow, payload flow, and generic update handlers.
|
||||||
|
- Explicit `MsgContext` field comments for update-backed, callback-backed, and message-backed contexts.
|
||||||
|
- Table-driven regression coverage for the normalized update contract, including callback target semantics and non-command update flows.
|
||||||
|
|
||||||
|
Practical target:
|
||||||
|
|
||||||
|
- Make update routing and `MsgContext` guarantees explicit enough to serve as a stable `1.0` public contract.
|
||||||
|
|
||||||
### [1.0.0-rc.12] Conversation / Scene Model
|
### [1.0.0-rc.12] Conversation / Scene Model
|
||||||
|
|
||||||
Current state:
|
Current state:
|
||||||
|
|||||||
Reference in New Issue
Block a user