В данной транзакции уже происходили ошибки: как определить, какой объект не смог записаться

Программист 1С v8.3 (Управляемые формы) IT и автоматизация бизнеса
← На главную

При работе с крупными обменами данными или сложными бизнес-процессами в 1С программисты часто сталкиваются с крайне неинформативной ошибкой: «В данной транзакции уже происходили ошибки!». Ситуация осложняется тем, что платформа выдает это сообщение не в момент возникновения реальной проблемы, а позже — когда транзакция уже «сломана», но код пытается продолжить работу с базой данных. В этой статье мы подробно разберем природу этой ошибки и научимся находить «виновника» — конкретный объект, из-за которого вся загрузка пошла прахом.

Разберем механизм возникновения ошибки

Прежде чем переходить к практике, нам важно понять теорию. Ошибка «В данной транзакции уже происходили ошибки!» (в среде SQL Server известная как The transaction is in an uncommittable state) возникает в строго определенном сценарии. Рассмотрим его по шагам:

  1. Программа открывает транзакцию (явно через НачатьТранзакцию() или неявно при записи объекта).
  2. Внутри этой транзакции происходит исключение или устанавливается флаг Отказ = Истина в обработчике события (например, ПередЗаписью).
  3. Если это исключение было перехвачено блоком Попытка ... Исключение, но транзакция не была отменена немедленно, она переходит в состояние «битой» (нефиксируемой).
  4. Любая последующая попытка обращения к базе данных (чтение или запись) в рамках этой же транзакции приведет к выбросу системного сообщения о том, что ошибки уже происходили.

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

Способ 1: Использование стандартной отладки «Остановка по ошибке»

Это самый первый и простой метод, который стоит попробовать. Наша задача — поймать ошибку в момент ее первичного возникновения, а не тогда, когда платформа сообщает о «битой» транзакции. Проанализируем, как это сделать:

  1. Откройте конфигуратор и перейдите в меню Отладка — Настройка исключений (или Остановка по ошибке).
  2. Установите флаг Останавливаться по ошибке.
  3. Обязательно снимите флаг Игнорировать ошибки в блоках попытка-исключение, если он установлен. Это критично, так как многие ошибки при обмене данными подавляются именно внутри блоков Попытка.
  4. Запустите процесс загрузки данных.

Когда отладчик прервет выполнение, посмотрите на стек вызовов. Вы окажетесь ровно в том месте, где возник первый сбой (например, не заполнено обязательное поле или сработала проверка уникальности номера). Объект, который находится в памяти в этот момент, и есть наш «виновник».

Способ 2: Настройка Технологического журнала (ТЖ)

Бывают случаи, когда ошибку невозможно воспроизвести под отладкой (например, она возникает только на сервере в регламентном задании). Тогда на помощь приходит Технологический журнал. Мы можем настроить сбор событий EXCP, которые регистрируют все исключительные ситуации.

Для этого создадим файл logcfg.xml в каталоге C:\Program Files\1cv8\conf (для конкретной версии платформы) со следующим содержанием:


<config xmlns="http://v8.1c.ru/7.4/config">
  <log location="C:\LOGS\TRANS_ERRORS" history="2">
    <event>
      <eq property="Name" value="EXCP"/>
    </event>
    <event>
      <eq property="Name" value="DBV8DBENG"/>
    </event>
    <property name="All"/>
  </log>
</config>

Что мы ищем в логах? Проанализируем файлы журналов, которые появятся в папке C:\LOGS\TRANS_ERRORS. Нам нужно найти первое событие EXCP по времени в рамках вашего сеанса. В описании события часто можно увидеть текст ошибки СУБД или сообщение от самой платформы 1С. Например, там может быть указано имя таблицы (например, _Reference150), что позволит через ПолучитьСтруктуруХраненияБазыДанных() вычислить конкретный справочник.

Способ 3: Режим отладки в правилах обмена (КД 2.0 / 3.0)

Если вы используете обработку УниверсальныйОбменДаннымиXML, то проблема часто скрыта в обработчиках «ПередЗаписью» или «ПриЗагрузке». Чтобы увидеть реальное описание ошибки, а не системное сообщение о транзакции, выполните следующие действия:

  1. В обработке загрузки данных перейдите на вкладку Настройки.
  2. Установите флаг Режим отладки обработчиков загрузки.
  3. При возникновении ошибки система предложит создать внешнюю обработку для отладки или выведет подробное описание исключения.

Это позволит платформе игнорировать общую ошибку транзакции и показать конкретный текст, например: «Поле 'Склад' не заполнено». Вы сразу поймете, какой объект (документ или справочник) пришел с неполными данными.

Способ 4: Программный контроль транзакции

Если вы сами пишете код загрузки, проанализируем правильный подход к обработке ошибок. Рассмотрим пример кода, который часто приводит к проблеме, и как его исправить. Рассмотрим плохой пример:


НачатьТранзакцию();
Попытка
    Объект.Записать();
Исключение
    // ОШИБКА: Мы просто вывели сообщение, но транзакция осталась открытой и "битой"
    Сообщить("Не удалось записать объект: " + ОписаниеОшибки());
КонецПопытки;
// Следующее действие вызовет ошибку "В данной транзакции уже происходили ошибки"
ДругойОбъект.Записать(); 
ЗафиксироватьТранзакцию();

А теперь изучим, как этот код должен выглядеть правильно:


НачатьТранзакцию();
Попытка
    Объект.Записать();
Исключение
    // ПРАВИЛЬНО: Сначала отменяем транзакцию, чтобы освободить ресурсы и сбросить статус ошибки
    Если ТранзакцияАктивна() Тогда
        ОтменитьТранзакцию();
    КонецЕсли;
    Сообщить("Запись прервана: " + ОписаниеОшибки());
    Возврат; // Прекращаем выполнение, так как транзакция уже невалидна
КонецПопытки;
ЗафиксироватьТранзакцию();

Способ 5: Прикладное решение через свойство «Загрузка»

Часто ошибки в транзакциях возникают из-за избыточных проверок в модулях объектов (проверки остатков, заполнение реквизитов, подписки на события). Если ваша задача — просто загрузить данные из другой системы, проанализируем использование свойства ОбменДанными.Загрузка.

Установите перед записью объекта следующий код:

Объект.ОбменДанными.Загрузка = Истина;

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

Выясним причину через SQL Profiler (для продвинутых)

Если у вас есть доступ к SQL серверу, мы можем запустить SQL Profiler. Это позволит увидеть, на какой именно инструкции INSERT или UPDATE сервер вернул ошибку. Проанализируйте события Exception и User Error Message. В тексте события часто будет указан идентификатор записи или нарушения уникальности индекса, что является прямой уликой против конкретного объекта базы данных.

Подведем итог: чтобы найти «виновного» объекта, не нужно смотреть на само сообщение об ошибке транзакции. Нужно искать самое первое исключение, произошедшее в рамках процесса. Используйте для этого «Остановку по ошибке» в конфигураторе или Технологический журнал, и вы быстро локализуете проблему.

← На главную