Как обрабатывать ошибки выполнения в коде 1С

· 9 мин

Содержание

Исключение в 1С — это не галочка в настройках, а механизм языка: конструкция Попытка … Исключение … КонецПопытки, которая перехватывает ошибку выполнения и передаёт управление вашему коду. Настроить обработку исключений в 1С 8.3 значит расставить такие блоки там, где операция реально может сорваться (запись объекта, транзакция, обмен, обращение к внешнему сервису), и решить, что делать после перехвата.

Частая ошибка начинающего — обернуть Попытку вокруг половины модуля и оставить блок Исключение пустым. Ошибка молча проглатывается, данные остаются в полуобработанном состоянии, а в журнале регистрации пусто — разобраться потом невозможно. Перехват без обработки хуже, чем падение с внятным текстом.

Когда платформа порождает исключение

Исключение возникает при ошибке времени выполнения, которую платформа не может продолжить. Типичные источники:

  • 🔸 обращение к несуществующему реквизиту или методу у значения Неопределено;
  • 🔸 нарушение уникальности при записи (дубль ключевых полей);
  • 🔸 конфликт блокировок в транзакции;
  • 🔸 недостаток прав у текущего пользователя;
  • 🔸 обрыв связи при обмене или вызове внешнего сервиса;
  • 🔸 деление на ноль, выход за границы массива, ошибка приведения типа.

Когда ошибка происходит, платформа прерывает выполнение и «разматывает» стек вызовов вверх — от текущей процедуры к той, что её вызвала, — пока не найдёт ближайший блок Исключение. Если по дороге такого блока нет, выполнение останавливается, а пользователь видит служебное окно с текстом ошибки.

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

Отдельно про транзакции: исключение внутри транзакции не откатывает её автоматически. Откат делаете вы — вызовом ОтменитьТранзакцию() в блоке Исключение. Если этого не сделать, платформа всё равно не даст зафиксировать битую транзакцию, но вы получите вдобавок ошибку о неверной последовательности вызовов.

Конструкция Попытка … Исключение … КонецПопытки

Базовый синтаксис перехвата выглядит так:

Попытка
    Документ.Записать();
Исключение
    // ошибка перехвачена — решаем, что делать
    ТекстОшибки = ОписаниеОшибки();
    ЗаписьЖурналаРегистрации("Проведение", УровеньЖурналаРегистрации.Ошибка, , , ТекстОшибки);
    ВызватьИсключение;
КонецПопытки;

Что здесь важно по сути:

  1. В блок Попытка помещают только ту операцию, которая может упасть, — не весь метод целиком. Чем уже блок, тем точнее вы знаете, что именно перехватили.
  2. В блоке Исключение доступен контекст ошибки (ОписаниеОшибки(), ИнформацияОбОшибке()).
  3. Оператор ВызватьИсключение; без параметров внутри блока Исключение пробрасывает текущую ошибку дальше по стеку, не теряя исходный текст. Это нужно, когда вы хотите залогировать проблему, но всё равно прервать операцию.

Сгенерировать собственное исключение можно оператором с текстом:

Если Не ЗначениеЗаполнено(Контрагент) Тогда
    ВызватьИсключение "Не указан контрагент — проведение невозможно.";
КонецЕсли;

Блоки Попытка можно вкладывать друг в друга: внутренний перехватывает частную ошибку, внешний — всё остальное. Но не превращайте это в лабиринт — два-три уровня вложенности обычно предел читаемости.

Как достать текст и причину ошибки

В 1С есть два способа получить информацию об ошибке в блоке Исключение.

Старый способ — функция ОписаниеОшибки(). Возвращает строку с текстом. Работает везде, пригодна для короткого лога, но не даёт структуру (модуль, строку, цепочку причин).

Современный способ — функция ИнформацияОбОшибке(). Возвращает объект со свойствами:

СвойствоЧто содержит
Описаниетекст ошибки
Причинавложенный объект ИнформацияОбОшибке — исходная ошибка в цепочке
ИмяМодулямодуль, где возникла ошибка
НомерСтрокиномер строки в модуле
ИсходнаяСтрокатекст строки кода

Для вывода текста по объекту используют глобальные функции КраткоеПредставлениеОшибки() и ПодробноеПредставлениеОшибки():

Попытка
    ВыполнитьЗагрузкуДанных();
Исключение
    Инфо = ИнформацияОбОшибке();
    Кратко   = КраткоеПредставлениеОшибки(Инфо);   // для пользователя
    Подробно = ПодробноеПредставлениеОшибки(Инфо);  // для журнала
    ЗаписьЖурналаРегистрации("Загрузка", УровеньЖурналаРегистрации.Ошибка, , , Подробно);
    ВызватьИсключение Кратко;
КонецПопытки;

Не показывайте пользователю ПодробноеПредставлениеОшибки() целиком — там имена модулей, номера строк и внутренняя кухня. Пользователю — короткий понятный текст, в журнал — подробный.

Объект ИнформацияОбОшибке доступен начиная с платформы 8.3.7; в более ранних релизах остаётся только ОписаниеОшибки(). Если целевая платформа — не ниже 8.3.7, используйте объектный вариант: цепочка Причина сильно экономит время при разборе ошибок в обменах и вложенных вызовах.

Запись ошибок в журнал регистрации

Журнал регистрации — штатное место для логов. Писать в него нужно в блоке Исключение, пока контекст ошибки ещё доступен. Функция:

ЗаписьЖурналаРегистрации(
    "Обмен.Загрузка",                        // событие — ваша строка-метка
    УровеньЖурналаРегистрации.Ошибка,         // уровень важности
    ,                                         // метаданные (объект)
    ,                                         // данные (ссылка на объект)
    ПодробноеПредставлениеОшибки(ИнформацияОбОшибке()));

Уровни важности задаются перечислением УровеньЖурналаРегистрации: Ошибка, Предупреждение, Информация, Примечание. Для пойманных исключений ставьте Ошибка — так их легко отфильтровать при разборе.

Где смотреть журнал:

  • 🔹 в конфигураторе — меню Администрирование → Журнал регистрации;
  • 🔹 в режиме 1С:Предприятие — через Все функции → Стандартные → Журнал регистрации (команда «Все функции» включается в параметрах пользователя).

В списке удобно отобрать записи по уровню «Ошибка» и по вашей метке события — тогда вы сразу видите, где и когда операция сорвалась, с полным техническим описанием.

Поиск источника в отладчике

Когда ошибка воспроизводится, но причина неочевидна, помогает отладчик конфигуратора.

  1. Поставьте точку останова на подозрительной строке — клавишей F9 или щелчком по левому полю редактора.
  2. Запустите сеанс с отладкой (Отладка → Начать отладку, клавиша F5).
  3. Когда выполнение остановится на точке, идите пошагово: F11 — с заходом в вызываемые процедуры, F10 — через них.
  4. Значения переменных смотрите в табло или наведением курсора; окно Стек вызовов показывает всю цепочку процедур, которая привела к текущей строке.

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

Стек вызовов можно получить и программно — функцией ПолучитьДанныеСтекаВызовов(). Она возвращает массив кадров стека и доступна в актуальных релизах платформы; запись такого стека в журнал в момент ошибки сильно ускоряет разбор инцидентов, которые у разработчика «не повторяются».

Диагностика: выберите симптом

Исключения в управляемых формах

В управляемом приложении код делится на клиентский и серверный контексты, и это меняет работу с ошибками.

Исключение, возникшее в процедуре &НаСервере, при вызове с клиента передаётся на клиента и прерывает клиентскую операцию. Если вы ничего не сделали — пользователь увидит серверный текст ошибки как есть. Правильный приём: перехватить ошибку на сервере, записать подробности в журнал и пробросить клиенту короткий понятный текст.

&НаСервере
Процедура ВыполнитьНаСервере()
    Попытка
        ОбработатьДокументы();
    Исключение
        ЗаписьЖурналаРегистрации("Обработка", УровеньЖурналаРегистрации.Ошибка, , ,
            ПодробноеПредставлениеОшибки(ИнформацияОбОшибке()));
        ВызватьИсключение "Не удалось обработать документы. Подробности записаны в журнал регистрации.";
    КонецПопытки;
КонецПроцедуры

На клиенте этот короткий текст можно поймать и показать неблокирующим способом — ПоказатьПредупреждение() — вместо того чтобы прерывать работу формы служебным окном. Сообщения, адресованные конкретному полю формы, формируйте объектом СообщениеПользователю с привязкой через УстановитьДанные/Поле — тогда пользователь видит, где именно ошибка, а не абстрактный текст вверху формы.

Не перехватывайте исключение на клиенте только ради того, чтобы «спрятать» его. Если операцию нельзя завершить — честный короткий текст полезнее тишины.

Транзакции и повторные попытки при блокировках

Связка «транзакция + исключение» — основа надёжной записи. Шаблон, который стоит держать в голове:

НачатьТранзакцию();
Попытка
    // запись объектов, движения, регистры
    ЗафиксироватьТранзакцию();
Исключение
    ОтменитьТранзакцию();
    ВызватьИсключение;
КонецПопытки;

Здесь откат делается явно в блоке Исключение, а исходная ошибка пробрасывается дальше — вызывающий код узнает, что фиксация не состоялась.

Помните про особенность вложенных транзакций в 1С: синтаксически их можно вкладывать, но физически она одна. Фиксация срабатывает только на самом внешнем ЗафиксироватьТранзакцию(), а любой ОтменитьТранзакцию() помечает на откат всю транзакцию целиком. Поэтому перехват исключения во внутреннем блоке не «спасёт» внешнюю транзакцию — после отмены на любом уровне фиксация внешней всё равно не пройдёт.

Отдельная техника — повторные попытки. Конфликт блокировок часто временный: два сеанса одновременно зацепили одни данные, один подождёт — и пройдёт. Такую операцию имеет смысл повторить несколько раз:

МаксПопыток = 3;
Ном = 0;
Пока Истина Цикл
    Ном = Ном + 1;
    Попытка
        НачатьТранзакцию();
        ОбработатьПорциюДанных();
        ЗафиксироватьТранзакцию();
        Прервать;
    Исключение
        ОтменитьТранзакцию();
        Если Ном >= МаксПопыток Тогда
            ВызватьИсключение;
        КонецЕсли;
    КонецПопытки;
КонецЦикла;

Повтор оправдан только для «нестабильных» ошибок — конфликт блокировок, временный обрыв связи. Повторять операцию, которая падает из-за ошибки в логике (неверные данные, нарушение уникальности), бессмысленно — вы получите три одинаковых падения вместо одного. Отличайте «подожди и повтори» от «исправь и запусти заново».

Перед финальной проверкой кода пройдитесь по короткому списку — он отсекает самые частые промахи в обработке исключений.

Проверка кода с исключениями
Выполнено: 0 / 5

Когда обёртка Попытка только мешает

Перехват — не универсальный клей для любого кода. Есть ситуации, где Попытка вредит.

Не оборачивайте в Попытку то, падение чего означает баг. Если обращение к реквизиту падает, потому что вы ошиблись в имени, обёртка не починит ошибку — она её спрячет, и на этапе разработки вы потеряете время. На отладке код полезнее ронять с внятным текстом, чем молча глотать.

Не используйте исключения как проверку условий. Чтобы узнать, заполнено ли значение, есть элемент в коллекции или найден ли объект, применяйте штатные средства — ЗначениеЗаполнено(), Найти(), проверку Количество(). Ловить ради этого исключение дороже по производительности и запутывает логику:

// плохо: исключение вместо проверки
Попытка
    Значение = Соответствие[Ключ];
Исключение
    Значение = Неопределено;
КонецПопытки;

// хорошо: штатная проверка
Значение = Соответствие.Получить(Ключ);

Попытка — не замена оператору Если и не инструмент управления потоком. Если вы выходите из блока через ВызватьИсключение в штатном сценарии, это сигнал пересмотреть логику.

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

Частые вопросы

Можно ли оставить блок Исключение пустым?
Технически да, но это почти всегда ошибка: вы перехватываете сбой и делаете вид, что его не было. Минимум — запишите ошибку в журнал регистрации. Если операцию нельзя продолжить — пробросьте исключение дальше через ВызватьИсключение.
Чем ОписаниеОшибки отличается от ИнформацияОбОшибке?
ОписаниеОшибки() возвращает только строку с текстом. ИнформацияОбОшибке() — объект с модулем, строкой, исходным кодом и цепочкой причин (Причина). Для разбора сложных ошибок берите объектный вариант (платформа 8.3.7 и новее).
Как пробросить ошибку дальше, не потеряв исходный текст?
Внутри блока Исключение вызовите ВызватьИсключение; без параметров — текущая ошибка уйдёт вверх по стеку в неизменном виде. Если напишете ВызватьИсключение "свой текст", исходный текст заменится на ваш.
Почему транзакция не откатывается сама при ошибке?
Платформа оставляет решение за вами: исключение может быть ожидаемым, и откат делаете вы вызовом ОтменитьТранзакцию() в блоке Исключение. Без явного отката зафиксировать транзакцию не получится, а последующие вызовы дадут ошибку о неверной последовательности.
Перехватывает ли Попытка ошибки платформы вроде деления на ноль?
Да, ошибки времени выполнения (деление на ноль, обращение к несуществующему реквизиту, выход за границы массива) перехватываются так же, как любые другие. Не перехватываются фатальные сбои процесса — но это уже не уровень прикладного кода.