Как обрабатывать ошибки выполнения в коде 1С
Содержание
Исключение в 1С — это не галочка в настройках, а механизм языка: конструкция Попытка … Исключение … КонецПопытки, которая перехватывает ошибку выполнения и передаёт управление вашему коду. Настроить обработку
исключений в 1С 8.3 значит расставить такие блоки там, где операция реально может сорваться (запись объекта, транзакция, обмен, обращение к внешнему сервису), и решить, что делать после перехвата.
Частая ошибка начинающего — обернуть Попытку вокруг половины модуля и оставить блок Исключение пустым. Ошибка молча проглатывается, данные остаются в полуобработанном состоянии, а в журнале регистрации пусто — разобраться потом невозможно. Перехват без обработки хуже, чем падение с внятным текстом.
Когда платформа порождает исключение
Исключение возникает при ошибке времени выполнения, которую платформа не может продолжить. Типичные источники:
- 🔸 обращение к несуществующему реквизиту или методу у значения
Неопределено; - 🔸 нарушение уникальности при записи (дубль ключевых полей);
- 🔸 конфликт блокировок в транзакции;
- 🔸 недостаток прав у текущего пользователя;
- 🔸 обрыв связи при обмене или вызове внешнего сервиса;
- 🔸 деление на ноль, выход за границы массива, ошибка приведения типа.
Когда ошибка происходит, платформа прерывает выполнение и «разматывает» стек вызовов вверх — от текущей процедуры к той, что её вызвала, — пока не найдёт ближайший блок Исключение. Если по дороге такого блока нет, выполнение останавливается, а пользователь видит служебное окно с текстом ошибки.
Исключение, возникшее в серверной процедуре, передаётся на клиента и показывается там — если его не перехватили на сервере. Поэтому длинный технический текст из серверного кода может «вылететь» прямо в лицо пользователю.
Отдельно про транзакции: исключение внутри транзакции не откатывает её автоматически. Откат делаете вы — вызовом ОтменитьТранзакцию() в блоке Исключение. Если этого не сделать, платформа всё равно не даст зафиксировать битую транзакцию, но вы получите вдобавок ошибку о неверной последовательности вызовов.
Конструкция Попытка … Исключение … КонецПопытки
Базовый синтаксис перехвата выглядит так:
Попытка
Документ.Записать();
Исключение
// ошибка перехвачена — решаем, что делать
ТекстОшибки = ОписаниеОшибки();
ЗаписьЖурналаРегистрации("Проведение", УровеньЖурналаРегистрации.Ошибка, , , ТекстОшибки);
ВызватьИсключение;
КонецПопытки;
Что здесь важно по сути:
- В блок
Попыткапомещают только ту операцию, которая может упасть, — не весь метод целиком. Чем уже блок, тем точнее вы знаете, что именно перехватили. - В блоке
Исключениедоступен контекст ошибки (ОписаниеОшибки(),ИнформацияОбОшибке()). - Оператор
ВызватьИсключение;без параметров внутри блокаИсключениепробрасывает текущую ошибку дальше по стеку, не теряя исходный текст. Это нужно, когда вы хотите залогировать проблему, но всё равно прервать операцию.
Сгенерировать собственное исключение можно оператором с текстом:
Если Не ЗначениеЗаполнено(Контрагент) Тогда
ВызватьИсключение "Не указан контрагент — проведение невозможно.";
КонецЕсли;
Блоки Попытка можно вкладывать друг в друга: внутренний перехватывает частную ошибку, внешний — всё остальное. Но не превращайте это в лабиринт — два-три уровня вложенности обычно предел читаемости.
Как достать текст и причину ошибки
В 1С есть два способа получить информацию об ошибке в блоке Исключение.
Старый способ — функция ОписаниеОшибки(). Возвращает строку с текстом. Работает везде, пригодна для короткого лога, но не даёт структуру (модуль, строку, цепочку причин).
Современный способ — функция ИнформацияОбОшибке(). Возвращает объект со свойствами:
| Свойство | Что содержит |
|---|---|
Описание | текст ошибки |
Причина | вложенный объект ИнформацияОбОшибке — исходная ошибка в цепочке |
ИмяМодуля | модуль, где возникла ошибка |
НомерСтроки | номер строки в модуле |
ИсходнаяСтрока | текст строки кода |
Для вывода текста по объекту используют глобальные функции КраткоеПредставлениеОшибки() и ПодробноеПредставлениеОшибки():
Попытка
ВыполнитьЗагрузкуДанных();
Исключение
Инфо = ИнформацияОбОшибке();
Кратко = КраткоеПредставлениеОшибки(Инфо); // для пользователя
Подробно = ПодробноеПредставлениеОшибки(Инфо); // для журнала
ЗаписьЖурналаРегистрации("Загрузка", УровеньЖурналаРегистрации.Ошибка, , , Подробно);
ВызватьИсключение Кратко;
КонецПопытки;
Не показывайте пользователю ПодробноеПредставлениеОшибки() целиком — там имена модулей, номера строк и внутренняя кухня. Пользователю — короткий понятный текст, в журнал — подробный.
Объект ИнформацияОбОшибке доступен начиная с платформы 8.3.7; в более ранних релизах остаётся только ОписаниеОшибки(). Если целевая платформа — не ниже 8.3.7, используйте объектный вариант: цепочка Причина сильно экономит время при разборе ошибок в обменах и вложенных вызовах.
Запись ошибок в журнал регистрации
Журнал регистрации — штатное место для логов. Писать в него нужно в блоке Исключение, пока контекст ошибки ещё доступен. Функция:
ЗаписьЖурналаРегистрации(
"Обмен.Загрузка", // событие — ваша строка-метка
УровеньЖурналаРегистрации.Ошибка, // уровень важности
, // метаданные (объект)
, // данные (ссылка на объект)
ПодробноеПредставлениеОшибки(ИнформацияОбОшибке()));
Уровни важности задаются перечислением УровеньЖурналаРегистрации: Ошибка, Предупреждение, Информация, Примечание. Для пойманных исключений ставьте Ошибка — так их легко отфильтровать при разборе.
Где смотреть журнал:
- 🔹 в конфигураторе — меню
Администрирование → Журнал регистрации; - 🔹 в режиме 1С:Предприятие — через
Все функции → Стандартные → Журнал регистрации(команда «Все функции» включается в параметрах пользователя).
В списке удобно отобрать записи по уровню «Ошибка» и по вашей метке события — тогда вы сразу видите, где и когда операция сорвалась, с полным техническим описанием.
Поиск источника в отладчике
Когда ошибка воспроизводится, но причина неочевидна, помогает отладчик конфигуратора.
- Поставьте точку останова на подозрительной строке — клавишей
F9или щелчком по левому полю редактора. - Запустите сеанс с отладкой (
Отладка → Начать отладку, клавишаF5). - Когда выполнение остановится на точке, идите пошагово:
F11— с заходом в вызываемые процедуры,F10— через них. - Значения переменных смотрите в табло или наведением курсора; окно
Стек вызововпоказывает всю цепочку процедур, которая привела к текущей строке.
Чтобы отлаживать код, работающий на сервере (в клиент-серверном варианте), отладку по протоколу нужно разрешить в настройках информационной базы и подключиться к серверным предметам отладки — иначе точки останова в серверных модулях срабатывать не будут.
Стек вызовов можно получить и программно — функцией ПолучитьДанныеСтекаВызовов(). Она возвращает массив кадров стека и доступна в актуальных релизах платформы; запись такого стека в журнал в момент ошибки сильно ускоряет разбор инцидентов, которые у разработчика «не повторяются».
Исключения в управляемых формах
В управляемом приложении код делится на клиентский и серверный контексты, и это меняет работу с ошибками.
Исключение, возникшее в процедуре &НаСервере, при вызове с клиента передаётся на клиента и прерывает клиентскую операцию. Если вы ничего не сделали — пользователь увидит серверный текст ошибки как есть. Правильный приём: перехватить ошибку на сервере, записать подробности в журнал и пробросить клиенту короткий понятный текст.
&НаСервере
Процедура ВыполнитьНаСервере()
Попытка
ОбработатьДокументы();
Исключение
ЗаписьЖурналаРегистрации("Обработка", УровеньЖурналаРегистрации.Ошибка, , ,
ПодробноеПредставлениеОшибки(ИнформацияОбОшибке()));
ВызватьИсключение "Не удалось обработать документы. Подробности записаны в журнал регистрации.";
КонецПопытки;
КонецПроцедуры
На клиенте этот короткий текст можно поймать и показать неблокирующим способом — ПоказатьПредупреждение() — вместо того чтобы прерывать работу формы служебным окном. Сообщения, адресованные конкретному полю формы, формируйте объектом СообщениеПользователю с привязкой через УстановитьДанные/Поле — тогда пользователь видит, где именно ошибка, а не абстрактный текст вверху формы.
Не перехватывайте исключение на клиенте только ради того, чтобы «спрятать» его. Если операцию нельзя завершить — честный короткий текст полезнее тишины.
Транзакции и повторные попытки при блокировках
Связка «транзакция + исключение» — основа надёжной записи. Шаблон, который стоит держать в голове:
НачатьТранзакцию();
Попытка
// запись объектов, движения, регистры
ЗафиксироватьТранзакцию();
Исключение
ОтменитьТранзакцию();
ВызватьИсключение;
КонецПопытки;
Здесь откат делается явно в блоке Исключение, а исходная ошибка пробрасывается дальше — вызывающий код узнает, что фиксация не состоялась.
Помните про особенность вложенных транзакций в 1С: синтаксически их можно вкладывать, но физически она одна. Фиксация срабатывает только на самом внешнем ЗафиксироватьТранзакцию(), а любой ОтменитьТранзакцию() помечает на откат всю транзакцию целиком. Поэтому перехват исключения во внутреннем блоке не «спасёт» внешнюю транзакцию — после отмены на любом уровне фиксация внешней всё равно не пройдёт.
Отдельная техника — повторные попытки. Конфликт блокировок часто временный: два сеанса одновременно зацепили одни данные, один подождёт — и пройдёт. Такую операцию имеет смысл повторить несколько раз:
МаксПопыток = 3;
Ном = 0;
Пока Истина Цикл
Ном = Ном + 1;
Попытка
НачатьТранзакцию();
ОбработатьПорциюДанных();
ЗафиксироватьТранзакцию();
Прервать;
Исключение
ОтменитьТранзакцию();
Если Ном >= МаксПопыток Тогда
ВызватьИсключение;
КонецЕсли;
КонецПопытки;
КонецЦикла;
Повтор оправдан только для «нестабильных» ошибок — конфликт блокировок, временный обрыв связи. Повторять операцию, которая падает из-за ошибки в логике (неверные данные, нарушение уникальности), бессмысленно — вы получите три одинаковых падения вместо одного. Отличайте «подожди и повтори» от «исправь и запусти заново».
Перед финальной проверкой кода пройдитесь по короткому списку — он отсекает самые частые промахи в обработке исключений.
Когда обёртка Попытка только мешает
Перехват — не универсальный клей для любого кода. Есть ситуации, где Попытка вредит.
Не оборачивайте в Попытку то, падение чего означает баг. Если обращение к реквизиту падает, потому что вы ошиблись в имени, обёртка не починит ошибку — она её спрячет, и на этапе разработки вы потеряете время. На отладке код полезнее ронять с внятным текстом, чем молча глотать.
Не используйте исключения как проверку условий. Чтобы узнать, заполнено ли значение, есть элемент в коллекции или найден ли объект, применяйте штатные средства — ЗначениеЗаполнено(), Найти(), проверку Количество(). Ловить ради этого исключение дороже по производительности и запутывает логику:
// плохо: исключение вместо проверки
Попытка
Значение = Соответствие[Ключ];
Исключение
Значение = Неопределено;
КонецПопытки;
// хорошо: штатная проверка
Значение = Соответствие.Получить(Ключ);
Попытка — не замена оператору Если и не инструмент управления потоком. Если вы выходите из блока через ВызватьИсключение в штатном сценарии, это сигнал пересмотреть логику.
Осторожнее с широкими блоками в длинных циклах. Обёртка вокруг тела цикла на десятки тысяч итераций и перехватывает лишнее, и мешает понять, на каком элементе всё сорвалось. Если без перехвата в цикле не обойтись — логируйте в блоке Исключение ключ или номер проблемного элемента, иначе разбирать инцидент придётся вслепую.
Частые вопросы
Можно ли оставить блок Исключение пустым?
ВызватьИсключение.Чем ОписаниеОшибки отличается от ИнформацияОбОшибке?
ОписаниеОшибки() возвращает только строку с текстом. ИнформацияОбОшибке() — объект с модулем, строкой, исходным кодом и цепочкой причин (Причина). Для разбора сложных ошибок берите объектный вариант (платформа 8.3.7 и новее).Как пробросить ошибку дальше, не потеряв исходный текст?
Исключение вызовите ВызватьИсключение; без параметров — текущая ошибка уйдёт вверх по стеку в неизменном виде. Если напишете ВызватьИсключение "свой текст", исходный текст заменится на ваш.Почему транзакция не откатывается сама при ошибке?
ОтменитьТранзакцию() в блоке Исключение. Без явного отката зафиксировать транзакцию не получится, а последующие вызовы дадут ошибку о неверной последовательности.