# Ошибки

- Только стандартный `errors` + `fmt.Errorf`. Сторонних пакетов ошибок нет:
  контекст несёт `slog`, стек-трейсы для домашнего сервиса избыточны.
- Контекст добавляем обёрткой `%w` — это дефолт, чтобы `errors.Is`/`As`
  работали сквозь слои. `%v` — только когда причину сознательно не
  раскрываем.
- Стиль сообщения: со строчной, без точки, без «failed to». Контекст —
  операция или субъект (`"open archive: %w"`), каждый слой добавляет **свой**
  смысл, не повторяя нижний.
- Граничные ошибки транслируем в доменные у источника: `sql.ErrNoRows` →
  `store.ErrNotFound` внутри `store`, чтобы выше не торчал `database/sql`.
- **Sentinel** (`var ErrNotFound = errors.New(...)`) — для условий, на которые
  ветвится код. **Типизированная ошибка** — когда вызывающему нужны данные
  ошибки. Не плодим типы там, где хватает sentinel.
- Наружу (HTTP) отдаём человекочитаемое сообщение по доменной ошибке, не
  сырой `err.Error()`. Маппинг доменная ошибка → статус живёт в одной точке
  в `httpapi`; новая штатная ветвь отказа заводится sentinel'ом и
  добавляется туда, иначе `default` отдаст 500 на нормальный конфликт.
- Собрать независимые ошибки (валидация конфига — все проблемы разом) —
  `errors.Join`.
- `panic` — только невосстановимое: нарушенный инвариант, сбой инициализации.
  `recover` — на верхней границе HTTP-обработчика.
- Глушить ошибку без лога — только с однострочным комментарием «почему».
