# Обработка ошибок: почему приложение ломается молча

> Обработка ошибок простыми словами: почему приложение ломается молча, что делать с ошибками на фронтенде и бэкенде и что показывать человеку, а что писать в лог.

Published: 2026-10-20 · Source: https://subxcat.com/ru/articles/error-handling-basics · Author: SUBXCAT

Обработка ошибок — это заранее решённый ответ на вопрос «что делать, если не получилось»: запрос не дошёл, база не ответила, человек ввёл не то. Приложение с хорошей обработкой ошибок говорит пользователю, что произошло и что делать дальше, а разработчику — где и почему.

В коде, написанном с ИИ, этот ответ часто отсутствует. Ассистент пишет то, что просили, — счастливый путь, где всё получается. Получается приложение, которое на демо работает, а у живого человека иногда просто ничего не делает. Кнопка нажата, спиннер крутится вечно, в базе пусто, и никто, включая тебя, об этом не знает.

## Почему приложение ломается молча

Три частые причины тишины:

- **Ошибку поймали и выбросили.** Пустой `catch {}` или `catch (e) { console.log(e) }` в браузере. Формально ошибка обработана, на деле — спрятана.
- **Ошибку не ждали вовсе.** Запрос к API без проверки ответа: сервер вернул `500`, а код читает данные так, будто всё в порядке, и показывает пустой список.
- **Ошибка случилась там, где никто не смотрит.** Фоновая задача, вебхук, функция по расписанию. Упала — и всё, следов нет.

Первое, что стоит сделать в таком проекте, — поиск по коду: `catch`, `.catch(`, `except`. Каждое место, где ошибка молча проглатывается, — кандидат на исправление.

## Два адресата: человек и лог

У каждой ошибки два читателя, и им нужно разное.

**Человеку** нужно понять, что произошло на его стороне и что делать: «Не удалось сохранить, проверь интернет и попробуй ещё раз», «Такой адрес уже зарегистрирован — войди или восстанови пароль». Без технических подробностей, без стека вызовов, без названий таблиц. Внутренности системы в сообщении об ошибке — это ещё и подсказка атакующему.

**Логу** нужно всё, чтобы разобраться потом: что за операция, какой пользователь (идентификатор, а не почта), какие входные параметры, полный текст ошибки и стек. Но без паролей, токенов и номеров карт.

Хорошая практика — одно и то же событие отправить в оба места: короткое понятное сообщение человеку и подробную запись в лог, связанные общим идентификатором. Тогда на письмо в поддержку «у меня ошибка, код A7F3» можно найти конкретную запись.

## Обработка ошибок на фронтенде

На стороне браузера задача — не оставлять человека в неизвестности.

- **У каждого запроса три состояния**: загрузка, успех и ошибка. Если в компоненте есть только первые два, третье случится всё равно — просто без интерфейса.
- **Проверяй ответ сервера.** `fetch` не считает ответ `404` или `500` ошибкой: нужно смотреть на `response.ok` самостоятельно.
- **Не теряй введённое.** Если отправка формы не удалась, данные должны остаться в полях. Заставить человека заново заполнять длинную форму — надёжный способ его потерять.
- **Ставь общую страховку.** В React это граница ошибок — компонент, который вместо белого экрана показывает «что-то пошло не так» с кнопкой «обновить».
- **Отправляй ошибки браузера к себе.** Без сервиса сбора ошибок ты не узнаешь, что у пользователей с определённым браузером всё падает.

## Обработка ошибок на бэкенде

На сервере задача — не врать о результате и не терять информацию.

- **Правильные коды ответа.** Ошибка ввода — `400`, нет доступа — `403`, не найдено — `404`, сломалось у нас — `500`. Ответ `200` с текстом «ошибка» внутри ломает всю логику на клиенте.
- **Проверка на входе.** Чем раньше отбракованы плохие данные, тем меньше странных ошибок глубже.
- **Внешние сервисы падают.** Ставь таймауты на запросы к ним и решай заранее, что будет при отказе: повторить, поставить в очередь или честно вернуть ошибку.
- **Не делай половину операции.** Если надо списать баланс и создать заказ, это одна транзакция: либо оба шага, либо ни одного.

Минимальный шаблон для обработчика выглядит примерно так:

```js
try {
  const order = await createOrder(input);
  return res.status(201).json(order);
} catch (err) {
  const id = crypto.randomUUID();
  logger.error({ id, userId: user.id, err }, "createOrder failed");
  return res.status(500).json({ error: "Не удалось оформить заказ", id });
}
```

## Что не надо делать

- Показывать человеку текст исключения как есть.
- Ловить все ошибки подряд и продолжать, как будто ничего не случилось.
- Бесконечно повторять запрос, который падает каждый раз, — особенно к платному API.
- Писать в лог всё тело запроса целиком, вместе с паролем.

## Коротко

- Обработка ошибок — заранее решённый ответ на «что, если не получилось».
- Самая опасная ошибка — проглоченная: пустой `catch` хуже упавшего приложения.
- Человеку — понятное сообщение и следующий шаг, логу — подробности без секретов, между ними — общий идентификатор.
- На фронтенде у каждого запроса есть состояние ошибки, а введённые данные не теряются.
- На бэкенде — честные коды ответа, таймауты на внешние сервисы и транзакции.

Если в проекте десятки пустых `catch`, SUBXCAT может разобрать их и настроить сбор ошибок, чтобы молчаливые поломки стали видимыми.
