بنقرة واحدة
error-handling
For BSL exceptions, transactions, rollbacks, and locks
التثبيت باستخدام Codex أو Claude انسخ هذا Prompt والصقه في Codex أو Claude أو مساعد آخر ليراجع صفحة Skill ويثبّتها لك.
القائمة
For BSL exceptions, transactions, rollbacks, and locks
التثبيت باستخدام Codex أو Claude انسخ هذا Prompt والصقه في Codex أو Claude أو مساعد آخر ليراجع صفحة Skill ويثبّتها لك.
استنادا إلى تصنيف SOC المهني
When writing or reviewing BSL, apply 1C standards
При написании или ревью BSL применять стандарты 1С
Orchestrator: routing work and agent phases
Оркестратор: маршрутизация работы и фаз агентов
BSL LSP navigation: definitions, refs, call graph
Rules for using RLM tools for project search and navigation in 1C/BSL
| name | error-handling |
| description | For BSL exceptions, transactions, rollbacks, and locks |
| alwaysApply | false |
Key principle: In 1C there is no automatic transaction management. The developer manually controls the start, commit, and rollback. Every unclosed transaction is a potential catastrophe.
A swallowed exception is the most dangerous anti-pattern: data is not written, the user does not know about the error, there are no traces in the registration log, debugging is impossible.
ITS standard: "In the exception handler, error information must be recorded in the registration log".
Попытка
ДокументОбъект.Записать(РежимЗаписиДокумента.Проведение);
Исключение
ИнформацияОбОшибке = ИнформацияОбОшибке();
ЗаписьЖурналаРегистрации(
НСтр("ru = 'Проведение документа'"),
УровеньЖурналаРегистрации.Ошибка,
ДокументОбъект.Метаданные(),
ДокументОбъект.Ссылка,
ПодробноеПредставлениеОшибки(ИнформацияОбОшибке));
ВызватьИсключение;
КонецПопытки;
| Function | When to use | What it contains |
|---|---|---|
КраткоеПредставлениеОшибки() | For display to the user | Clear text without technical details |
ПодробноеПредставлениеОшибки() | For the registration log | Full call stack, line numbers, nested errors |
// Lower level — logging + rethrow
Функция ЗаписатьДокумент(ДокументОбъект)
Попытка
ДокументОбъект.Записать(РежимЗаписиДокумента.Проведение);
Возврат Истина;
Исключение
ЗаписьЖурналаРегистрации(
НСтр("ru = 'Проведение документа'"),
УровеньЖурналаРегистрации.Ошибка,
ДокументОбъект.Метаданные(),
ДокументОбъект.Ссылка,
ПодробноеПредставлениеОшибки(ИнформацияОбОшибке()));
ВызватьИсключение;
КонецПопытки;
КонецФункции
// Upper level (form) — show to the user
&НаКлиенте
Процедура ЗаписатьДокумент(Команда)
Попытка
ЗаписатьДокументНаСервере();
Исключение
ПоказатьПредупреждение(,
НСтр("ru = 'Не удалось записать документ. Обратитесь к администратору.'"));
КонецПопытки;
КонецПроцедуры
An unclosed transaction blocks writes in the DBMS. Other sessions wait (timeout ~20 sec) and get an error.
ITS standard: “Transactions: usage rules” — НачатьТранзакцию() is ALWAYS immediately before Попытка.
НачатьТранзакцию();
Попытка
// 1. Data locking (if needed — see rule 5)
Блокировка = Новый БлокировкаДанных;
ЭлементБлокировки = Блокировка.Добавить("Документ.РеализацияТоваровУслуг");
ЭлементБлокировки.УстановитьЗначение("Ссылка", ДокументСсылка);
Блокировка.Заблокировать();
// 2. Reading and modifying data
ДокументОбъект = ДокументСсылка.ПолучитьОбъект();
ДокументОбъект.Статус = Перечисления.СтатусыДокументов.Согласован;
// 3. Writing
ДокументОбъект.Записать();
// === Commit — the LAST operation before Исключение ===
ЗафиксироватьТранзакцию();
Исключение
// === Rollback — the FIRST operation in the Исключение block ===
ОтменитьТранзакцию();
// Logging AFTER rollback (a write to the event log inside a rolled-back transaction will be lost!)
ЗаписьЖурналаРегистрации(
НСтр("ru = 'Согласование документа'"),
УровеньЖурналаРегистрации.Ошибка,
Метаданные.Документы.РеализацияТоваровУслуг,
ДокументСсылка,
ПодробноеПредставлениеОшибки(ИнформацияОбОшибке()));
ВызватьИсключение;
КонецПопытки;
| Requirement | Why |
|---|---|
НачатьТранзакцию() immediately before Попытка | If an error occurs between them, the transaction will not be closed |
ЗафиксироватьТранзакцию() is the last statement before Исключение | An operation after commit will not be rolled back if it fails |
ОтменитьТранзакцию() is the first statement in Исключение | Logging can also fail; if rollback has not been performed yet, that creates a cascading problem |
ЗаписьЖурналаРегистрации() is AFTER ОтменитьТранзакцию() | A write to the event log inside a rolled-back transaction will be lost |
// ПЛОХО: code between НачатьТранзакцию and Попытка
НачатьТранзакцию();
ПодготовитьДанные(); // If there is an error here, the transaction will hang!
Попытка
// ...
КонецПопытки;
// ПЛОХО: ЗаписьЖурнала BEFORE ОтменитьТранзакцию
Исключение
ЗаписьЖурналаРегистрации(...); // May be lost during rollback!
ОтменитьТранзакцию();
КонецПопытки;
// ПЛОХО: code after ЗафиксироватьТранзакцию, but before the end of Попытка
ЗафиксироватьТранзакцию();
ОтправитьОповещение(); // Error here - the transaction is already committed, but Исключение will run!
Исключение
ОтменитьТранзакцию(); // Error! The transaction is already committed!
КонецПопытки;
In 1С, a nested НачатьТранзакцию() does not create a new transaction, but increments the counter. ОтменитьТранзакцию() marks the transaction as "rolled back", and any subsequent ЗафиксироватьТранзакцию() (even at the outer level) will raise an exception.
Процедура ЗаписатьДанные(ДанныеДляЗаписи)
НачатьТранзакцию();
Попытка
ДокументОбъект.Записать();
ЗафиксироватьТранзакцию();
Исключение
ОтменитьТранзакцию();
ЗаписьЖурналаРегистрации(
НСтр("ru = 'Запись данных'"),
УровеньЖурналаРегистрации.Ошибка,,,
ПодробноеПредставлениеОшибки(ИнформацияОбОшибке()));
ВызватьИсключение; // ОБЯЗАТЕЛЬНО пробрасываем — внешний код должен знать
КонецПопытки;
КонецПроцедуры
// ПЛОХО: ТранзакцияАктивна() masks an error in the code structure
Попытка
НачатьТранзакцию();
// ...
ЗафиксироватьТранзакцию();
Исключение
Если ТранзакцияАктивна() Тогда
ОтменитьТранзакцию();
КонецЕсли;
КонецПопытки;
// ПРАВИЛЬНО: correct structure makes the check unnecessary
НачатьТранзакцию();
Попытка
// ...
ЗафиксироватьТранзакцию();
Исключение
ОтменитьТранзакцию();
ВызватьИсключение;
КонецПопытки;
While a transaction is open, the modified data is locked in the DBMS. A long transaction = cascading locks = users cannot work.
// Data preparation — OUTSIDE the transaction
МассивДанных = ПодготовитьДанные();
ПроверитьКорректность(МассивДанных);
// Transaction — only fast write operations
НачатьТранзакцию();
Попытка
Для Каждого ДанныеСтроки Из МассивДанных Цикл
ЗаписатьСтроку(ДанныеСтроки);
КонецЦикла;
ЗафиксироватьТранзакцию();
Исключение
ОтменитьТранзакцию();
ЗаписьЖурналаРегистрации(...);
ВызватьИсключение;
КонецПопытки;
External HTTP calls inside a transaction are a disaster: HTTP timeout = 30 sec = 30 sec lock.
Without granular locking before read-modify, a race condition occurs: two sessions read the same value, both modify it - one update is lost.
ITS standard: "Managed locks".
НачатьТранзакцию();
Попытка
// 1. FIRST we lock
Блокировка = Новый БлокировкаДанных;
ЭлементБлокировки = Блокировка.Добавить("РегистрНакопления.ТоварыНаСкладах");
ЭлементБлокировки.УстановитьЗначение("Номенклатура", НоменклатураСсылка);
ЭлементБлокировки.УстановитьЗначение("Склад", СкладСсылка);
ЭлементБлокировки.Режим = РежимБлокировкиДанных.Исключительный;
Блокировка.Заблокировать();
// 2. Read - guaranteed current data
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| Остатки.КоличествоОстаток КАК Остаток
|ИЗ
| РегистрНакопления.ТоварыНаСкладах.Остатки(,
| Номенклатура = &Номенклатура И Склад = &Склад) КАК Остатки";
Запрос.УстановитьПараметр("Номенклатура", НоменклатураСсылка);
Запрос.УстановитьПараметр("Склад", СкладСсылка);
Результат = Запрос.Выполнить();
Если Результат.Пустой() Тогда
ВызватьИсключение НСтр("ru = 'Нет остатков на складе.'");
КонецЕсли;
Выборка = Результат.Выбрать();
Выборка.Следующий();
// 3. Validate
Если Выборка.Остаток < ТребуемоеКоличество Тогда
ВызватьИсключение СтрШаблон(
НСтр("ru = 'Недостаточно остатков. На складе: %1, требуется: %2.'"),
Выборка.Остаток, ТребуемоеКоличество);
КонецЕсли;
// 4. Write
// ... запись движений ...
ЗафиксироватьТранзакцию();
Исключение
ОтменитьТранзакцию();
ЗаписьЖурналаРегистрации(...);
ВызватьИсключение;
КонецПопытки;
Без блокировки (race condition):
Сеанс A: Читает остаток = 10 | Сеанс B: Читает остаток = 10
Сеанс A: 10 >= 8? Да, списываем 8 | Сеанс B: 10 >= 7? Да, списываем 7
Итого: списано 15 единиц при остатке 10 → отрицательный остаток!
С блокировкой:
Сеанс A: Блокирует → Читает 10 → Списывает 8 → Фиксирует → Разблокирует
Сеанс B: Ждёт блокировку → Читает 2 → 2 < 7 → Ошибка (корректная!)
Prevents lost update: the second user will receive the error "The object is locked by user X".
Процедура ИзменитьСтатусДокумента(ДокументСсылка, НовыйСтатус)
НачатьТранзакцию();
Попытка
ЗаблокироватьДанныеДляРедактирования(ДокументСсылка);
ДокументОбъект = ДокументСсылка.ПолучитьОбъект();
ДокументОбъект.Статус = НовыйСтатус;
ДокументОбъект.Записать();
ЗафиксироватьТранзакцию();
Исключение
ОтменитьТранзакцию();
ЗаписьЖурналаРегистрации(
НСтр("ru = 'Изменение статуса документа'"),
УровеньЖурналаРегистрации.Ошибка,,,
ПодробноеПредставлениеОшибки(ИнформацияОбОшибке()));
ВызватьИсключение;
КонецПопытки;
КонецПроцедуры
| Lock type | Mechanism | When to use |
|---|---|---|
БлокировкаДанных | DBMS (managed), record level | Balance control, atomic operations |
ЗаблокироватьДанныеДляРедактирования | 1C server (pessimistic), whole object | Preventing lost update |
ЗаписьЖурналаРегистрации(
ИмяСобытия, // Строка — иерархическое имя (через точку)
УровеньСобытия, // УровеньЖурналаРегистрации — Ошибка/Предупреждение/Информация/Примечание
МетаданныеОбъекта, // Объект метаданных — для фильтрации по типу
Данные, // Ссылка на объект — для навигации из ЖР
Комментарий); // Строка — подробное описание (до 1024 символов)
| Level | When |
|---|---|
Error | Operation was not completed, data was lost or is invalid |
Warning | Operation completed, but with limitations |
Information | Significant events for auditing |
Note | Diagnostic information |
ИмяСобытия = НСтр("ru = 'ОбменДанными.ОтправкаДанных.Ошибка'");
Комментарий = СтрШаблон(
НСтр("ru = 'Ошибка при отправке данных в узел ""%1"".
|Количество объектов: %2.
|Текст ошибки:
|%3'"),
Строка(УзелОбмена),
КоличествоОбъектов,
ПодробноеПредставлениеОшибки(ИнформацияОбОшибке()));
ЗаписьЖурналаРегистрации(
ИмяСобытия,
УровеньЖурналаРегистрации.Ошибка,
Метаданные.ПланыОбмена.ОбменСКонтрагентами,
УзелОбмена,
Комментарий);
For the user - what happened and what to do. In the log - technical information.
Попытка
ДокументОбъект.Записать(РежимЗаписиДокумента.Проведение);
Исключение
ЗаписьЖурналаРегистрации(
НСтр("ru = 'Проведение документа'"),
УровеньЖурналаРегистрации.Ошибка,
ДокументОбъект.Метаданные(),
ДокументОбъект.Ссылка,
ПодробноеПредставлениеОшибки(ИнформацияОбОшибке()));
ТекстДляПользователя = СтрШаблон(
НСтр("ru = 'Не удалось провести документ ""%1"".
|Попробуйте повторить операцию. Если ошибка повторяется, обратитесь к администратору.
|
|Техническая информация: %2'"),
ДокументОбъект,
КраткоеПредставлениеОшибки(ИнформацияОбОшибке()));
ВызватьИсключение ТекстДляПользователя;
КонецПопытки;
| Method | When | Why |
|---|---|---|
ВызватьИсключение; | In intermediate code (object module, common module) | Preserves the original stack |
ВызватьИсключение "Текст"; | At the user boundary (form) | Replaces the technical stack with a clear message |
// Промежуточный слой — пробрасываем оригинал
Процедура ОбработатьДанные(Данные)
Попытка
ЗаписатьДанные(Данные);
Исключение
ЗаписьЖурналаРегистрации(...);
ВызватьИсключение; // Оригинальный стек сохранён
КонецПопытки;
КонецПроцедуры
// Граница с пользователем
&НаСервере
Процедура ОбработатьНаСервере()
Попытка
ОбработатьДанные(ДанныеФормы);
Исключение
ВызватьИсключение СтрШаблон(
НСтр("ru = 'Ошибка обработки данных: %1'"),
КраткоеПредставлениеОшибки(ИнформацияОбОшибке()));
КонецПопытки;
КонецПроцедуры
An error in one document must not stop processing the others. Each transaction is item-by-item.
Процедура ПровестиДокументыПакетно(МассивДокументов)
МассивОшибок = Новый Массив;
Для Каждого ДокументСсылка Из МассивДокументов Цикл
НачатьТранзакцию();
Попытка
ДокументОбъект = ДокументСсылка.ПолучитьОбъект();
ДокументОбъект.Записать(РежимЗаписиДокумента.Проведение);
ЗафиксироватьТранзакцию();
Исключение
ОтменитьТранзакцию();
ИнфоОшибки = ИнформацияОбОшибке();
ЗаписьЖурналаРегистрации(
НСтр("ru = 'Пакетное проведение'"),
УровеньЖурналаРегистрации.Ошибка,
ДокументСсылка.Метаданные(),
ДокументСсылка,
ПодробноеПредставлениеОшибки(ИнфоОшибки));
МассивОшибок.Добавить(Новый Структура("Документ, Ошибка",
ДокументСсылка,
КраткоеПредставлениеОшибки(ИнфоОшибки)));
КонецПопытки;
КонецЦикла;
Если МассивОшибок.Количество() > 0 Тогда
Для Каждого ОписаниеОшибки Из МассивОшибок Цикл
ОбщегоНазначения.СообщитьПользователю(
СтрШаблон(НСтр("ru = 'Документ %1: %2'"),
ОписаниеОшибки.Документ, ОписаниеОшибки.Ошибка));
КонецЦикла;
КонецЕсли;
КонецПроцедуры
When two sessions lock data in different orders - deadlock. The DBMS rolls back one of the transactions.
// Всегда блокируйте ресурсы в фиксированном порядке (по ссылке).
// У типа Массив нет метода сортировки — используйте СписокЗначений.СортироватьПоЗначению()
// (или ТаблицаЗначений.Сортировать()).
СписокСсылок = Новый СписокЗначений;
СписокСсылок.ЗагрузитьЗначения(ОбщегоНазначенияКлиентСервер.СвернутьМассив(МассивДокументов));
СписокСсылок.СортироватьПоЗначению();
Для Каждого ЭлементСписка Из СписокСсылок Цикл
ЗаблокироватьДанныеДляРедактирования(ЭлементСписка.Значение);
КонецЦикла;
НачатьТранзакцию();
Попытка
Блокировка = Новый БлокировкаДанных;
ЭлементБлокировки = Блокировка.Добавить("Справочник.Номенклатура");
ЭлементБлокировки.УстановитьЗначение("Ссылка", НоменклатураСсылка);
Попытка
Блокировка.Заблокировать();
Исключение
ОтменитьТранзакцию();
ВызватьИсключение СтрШаблон(
НСтр("ru = 'Не удалось заблокировать ""%1"". Данные редактируются другим пользователем.'"),
НоменклатураСсылка);
КонецПопытки;
НоменклатураОбъект = НоменклатураСсылка.ПолучитьОбъект();
НоменклатураОбъект.Записать();
ЗафиксироватьТранзакцию();
Исключение
Если ТранзакцияАктивна() Тогда
ОтменитьТранзакцию();
КонецЕсли;
ЗаписьЖурналаРегистрации(...);
ВызватьИсключение;
КонецПопытки;
Calls to external systems are unreliable. Always wrap them in Попытка/Исключение.
Функция ОтправитьДанныеВоВнешнююСистему(Данные)
МаксимумПопыток = 3;
Для НомерПопытки = 1 По МаксимумПопыток Цикл
Попытка
HTTPСоединение = Новый HTTPСоединение("api.example.com",,,,, 30);
Запрос = Новый HTTPЗапрос("/api/data");
Запрос.УстановитьТелоИзСтроки(Данные);
Ответ = HTTPСоединение.ОтправитьДляОбработки(Запрос);
Если Ответ.КодСостояния = 200 Тогда
Возврат Истина;
Иначе
ВызватьИсключение СтрШаблон(
НСтр("ru = 'Сервер вернул код %1: %2'"),
Ответ.КодСостояния,
Ответ.ПолучитьТелоКакСтроку());
КонецЕсли;
Исключение
ЗаписьЖурналаРегистрации(
НСтр("ru = 'Интеграция.ОтправкаДанных'"),
?(НомерПопытки < МаксимумПопыток,
УровеньЖурналаРегистрации.Предупреждение,
УровеньЖурналаРегистрации.Ошибка),,,
СтрШаблон(НСтр("ru = 'Попытка %1 из %2. Ошибка: %3'"),
НомерПопытки, МаксимумПопыток,
ПодробноеПредставлениеОшибки(ИнформацияОбОшибке())));
Если НомерПопытки = МаксимумПопыток Тогда
ВызватьИсключение;
КонецЕсли;
КонецПопытки;
КонецЦикла;
Возврат Ложь;
КонецФункции