Обращение через точку к пустому значению: как найти и убрать причину сбоя

· 9 мин

Содержание

Ошибка Значение не является значением объектного типа значит одно: код обратился через точку к свойству или методу значения, у которого свойств нет. Чаще всего это Неопределено, NULL, строка или число. Исправление — найти строку из текста ошибки, понять, почему переменная оказалась пустой, и поставить проверку до обращения через точку.

Полный текст обычно выглядит так: {ОбщийМодуль.ПродажиСервер.Модуль(214)}: Значение не является значением объектного типа (Контрагент). В фигурных скобках — модуль и номер строки, в круглых — имя свойства, к которому не удалось обратиться. Эти две подсказки уже дают половину решения. Поведение одинаково для всех конфигураций на платформе 1С 8.3: это ошибка встроенного языка, а не конкретной программы.

Почему 1С не может прочитать свойство: какие типы считаются объектными

Во встроенном языке у одних значений есть свойства и методы, у других нет. Обращение Переменная.Что-то работает только для первых.

ЗначениеЕсть свойства через точкуПример
Ссылка (СправочникСсылка, ДокументСсылка)✅ да, реквизиты объектаДоговор.Владелец
Объект (СправочникОбъект, ДокументОбъект)✅ даДокОбъект.Дата
Структура, строка таблицы значений, выборка✅ да, поля и колонкиВыборка.Сумма
Неопределено❌ нет—
NULL❌ нет—
Строка, Число, Дата, Булево❌ нет—

Когда в переменной оказывается значение из нижней части таблицы, платформа останавливает выполнение и показывает эту ошибку. В английском интерфейсе текст звучит как Value is not of object type.

💡 Пустая ссылка вроде Справочники.Контрагенты.ПустаяСсылка() — это всё ещё значение ссылочного типа. Обращение к её реквизиту возвращает пустое значение, а не эту ошибку. Поэтому проверка ЗначениеЗаполнено() и проверка на Неопределено решают разные задачи — об этом ниже.

Типичные ситуации, в которых возникает ошибка объектного типа

Почти все случаи сводятся к одной схеме: программист рассчитывал на объект, а получил пустоту. Вот откуда она берётся.

Поиск, который ничего не нашёл

Методы поиска в коллекциях возвращают Неопределено, если подходящего элемента нет:

  • 🔍 ТаблицаЗначений.Найти() и ДеревоЗначений.Строки.Найти();
  • 🔍 Массив.Найти() возвращает Неопределено вместо индекса;
  • 🔍 Соответствие.Получить() при отсутствии ключа.

Код Товары.Найти(Номенклатура, "Номенклатура").Количество упадёт ровно в тот момент, когда номенклатуры в таблице не окажется. На тестовых данных всё работало, у пользователя — нет.

NULL из запроса

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

Реквизит составного типа

Если реквизит может хранить и ссылку, и строку, то при строковом значении обращение Реквизит.Наименование сломается. Так часто бывает с полем «Субконто» или с реквизитами, где допустимы «Справочник.Контрагенты» и «Строка».

Функция вернула Неопределено

Функция, у которой есть ветка без Возврат, отдаёт Неопределено. То же с ПолучитьИзВременногоХранилища() по устаревшему адресу и с параметрами, которые не передали при вызове.

Табличная часть без выбранной строки

В управляемой форме Элементы.Товары.ТекущиеДанные равно Неопределено, когда строка не выделена или таблица пустая. Подробно — в разделе про формы.

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

Как найти причину ошибки отладчиком

Номер строки из текста ошибки показывает, где упало. Отладчик показывает, почему.

Включить остановку по ошибке

  1. Откройте базу в Конфигураторе.
  2. Запустите отладку: Отладка → Начать отладку и выберите тонкий клиент.
  3. В Конфигураторе включите Отладка → Остановка по ошибке. Флажок можно оставить для всех ошибок или задать текст Значение не является значением объектного типа, чтобы не останавливаться на остальных.
  4. Повторите в клиенте действие, которое вызывало сбой.

Выполнение остановится прямо на проблемной строке. Не придётся искать модуль вручную.

Посмотреть, что лежит в переменной

На строке остановки выделите выражение до точки и нажмите Shift+F9 — откроется окно вычисления выражения. Полезно проверить три вещи:

  • Переменная — что в ней сейчас;
  • ТипЗнч(Переменная) — какой тип пришёл на самом деле;
  • Переменная = Неопределено — простая проверка на пустоту.

Цепочку вроде Документ.Договор.Владелец.ИНН разбирайте по звеньям. Ошибка относится к последнему звену перед свойством из скобок. Если в скобках ИНН, пустым оказался Владелец.

Отладка на сервере

В файловой базе серверный код отлаживается сразу. В клиент-серверной базе отладка на сервере работает, только если служба агента сервера запущена с ключом -debug. Добавление ключа требует перезапуска службы.

Перезапуск службы 1С оборвёт сеансы всех пользователей кластера. Делайте это вне рабочего времени или на копии базы.

💡 Удобнее всего повторять сбой на копии рабочей базы. Тогда можно останавливаться на точках сколько угодно, не мешая бухгалтерии.

Журнал регистрации и технологический журнал при ошибке объектного типа

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

Журнал регистрации

В БП 3.0, ЗУП 3.1 и УТ 11 журнал открывается через Администрирование → Обслуживание → Журнал регистрации. В Конфигураторе — через Администрирование → Журнал регистрации.

Что настроить в отборе:

  1. Уровень — «Ошибка».
  2. Период — время, когда пользователь видел сообщение.
  3. Пользователь — тот, у кого возникла ошибка.

В комментарии записи будет полный текст с модулем и номером строки. По событию видно, что делал пользователь: проводил документ, открывал форму, запускал регламентное задание. Последнее особенно ценно: ошибки фоновых заданий пользователь вообще не видит.

Технологический журнал

Когда журнала регистрации мало, включите технологический журнал. Это файл logcfg.xml в каталоге conf платформы. Для поиска исключений достаточно собирать событие EXCP:

<?xml version="1.0" encoding="UTF-8"?>
<config xmlns="http://v8.1c.ru/v8/tech-log">
  <log location="C:\techlog" history="24">
    <event>
      <eq property="name" value="EXCP"/>
    </event>
    <property name="all"/>
  </log>
</config>

В записях EXCP есть текст исключения и контекст вызова. Журнал растёт быстро, поэтому history="24" ограничивает хранение сутками. После расследования удалите файл настройки.

Особенности управляемых форм: клиент и сервер

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

Текущие данные таблицы

Самый частый случай — обработчик строки табличной части:

&НаКлиенте
Процедура ТоварыПриАктивизацииСтроки(Элемент)

    ТекущиеДанные = Элементы.Товары.ТекущиеДанные;
    Если ТекущиеДанные = Неопределено Тогда
        Возврат;
    КонецЕсли;

    Элементы.ДекорацияЦена.Заголовок = Формат(ТекущиеДанные.Цена, "ЧДЦ=2");

КонецПроцедуры

Без проверки код ломается, когда пользователь удалил последнюю строку или открыл форму с пустой таблицей.

Реквизиты ссылок недоступны на клиенте

На клиенте ссылка — это просто идентификатор. Прочитать её реквизит через точку там нельзя: Объект.Контрагент.ИНН в процедуре с директивой &НаКлиенте вызовет ошибку. Данные нужно получить серверным вызовом:

&НаСервереБезКонтекста
Функция ИННКонтрагента(Контрагент)

    Возврат ОбщегоНазначения.ЗначениеРеквизитаОбъекта(Контрагент, "ИНН");

КонецФункции

ОбщегоНазначения.ЗначениеРеквизитаОбъекта() есть в типовых конфигурациях на БСП. Если нужно несколько полей, используйте ЗначенияРеквизитовОбъекта() — один вызов сервера вместо нескольких.

Объект формы — это не объект базы

Объект в форме — это данные формы, а не ДокументОбъект. Методы объекта вроде Записать() или собственные экспортные процедуры модуля объекта у него не вызвать. Для этого на сервере преобразуйте данные: РеквизитФормыВЗначение("Объект").

Как защитить код от ошибки «значение не является значением объектного типа»

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

СитуацияПриёмПример
Результат поиска, ТекущиеДанныеСравнение с НеопределеноЕсли Стр = Неопределено Тогда
Поле из запроса с соединениемЕСТЬNULL() в тексте запросаЕСТЬNULL(Цены.Цена, 0)
Реквизит составного типаПроверка типаТипЗнч(Знач) = Тип("СправочникСсылка.Контрагенты")
Любое значение, которое может быть пустымЗначениеЗаполнено()Если ЗначениеЗаполнено(Договор) Тогда
Сторонний код, на который нельзя повлиятьПопытка ... Исключениес записью в журнал

Проверка результата поиска

СтрокаТовара = Товары.Найти(Номенклатура, "Номенклатура");
Если СтрокаТовара = Неопределено Тогда
    СтрокаТовара = Товары.Добавить();
    СтрокаТовара.Номенклатура = Номенклатура;
КонецЕсли;
СтрокаТовара.Количество = СтрокаТовара.Количество + 1;

Цепочки через точку — лучше в запрос

Документ.Договор.Владелец.ИНН — три обращения к базе и три места для пустоты. В языке запросов то же поле через точку не падает: на пустом звене запрос вернёт NULL. Его остаётся обернуть в ЕСТЬNULL():

Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
|    ЕСТЬNULL(Док.Договор.Владелец.ИНН, """") КАК ИНН
|ИЗ
|    Документ.РеализацияТоваровУслуг КАК Док
|ГДЕ
|    Док.Ссылка = &Ссылка";
Запрос.УстановитьПараметр("Ссылка", Ссылка);

Бонус — запрос работает быстрее, чем цепочка обращений в цикле. Особенно заметно это на сотнях документов: каждое обращение через точку к реквизиту ссылки на сервере читает данные объекта, а запрос забирает всё одним обращением к базе.

Попытка без проглатывания ошибки

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

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

Функция всегда возвращает значение

Функция с несколькими ветками должна заканчиваться явным Возврат. Если по логике «ничего не найдено» — верните пустую ссылку нужного типа или Неопределено, но осознанно, и опишите это в комментарии к функции. Тогда вызывающий код знает, что результат надо проверять.

Функция ОсновнойДоговор(Контрагент)

    Если Не ЗначениеЗаполнено(Контрагент) Тогда
        Возврат Справочники.ДоговорыКонтрагентов.ПустаяСсылка();
    КонецЕсли;

    Запрос = Новый Запрос;
    Запрос.Текст =
    "ВЫБРАТЬ ПЕРВЫЕ 1
    |    Договоры.Ссылка КАК Ссылка
    |ИЗ
    |    Справочник.ДоговорыКонтрагентов КАК Договоры
    |ГДЕ
    |    Договоры.Владелец = &Контрагент
    |    И НЕ Договоры.ПометкаУдаления";
    Запрос.УстановитьПараметр("Контрагент", Контрагент);

    Выборка = Запрос.Выполнить().Выбрать();
    Если Выборка.Следующий() Тогда
        Возврат Выборка.Ссылка;
    КонецЕсли;

    Возврат Справочники.ДоговорыКонтрагентов.ПустаяСсылка();

КонецФункции

Пустая ссылка безопаснее Неопределено: обращение к её реквизитам не уронит код, а ЗначениеЗаполнено() покажет, что договора нет.

Не оборачивайте в Попытку свой собственный код вместо проверки на Неопределено — так ошибка превращается в тихо испорченные данные.

Если ошибка появилась в типовой конфигурации

Пользователю без навыков программиста не нужно лезть в код. Сначала определите по тексту ошибки, чей модуль упал.

  • 🧩 В тексте имя внешней обработки или отчёта — сбой в подключённой доработке. Найдите её в Администрирование → Печатные формы, отчеты и обработки и временно отключите.
  • 🧩 В тексте модуль расширения — отключите расширение в том же разделе и проверьте, ушла ли ошибка.
  • 🧩 Модуль типовой конфигурации — проверьте, установлен ли актуальный релиз. Подобные сбои разработчики типовых решений регулярно исправляют в обновлениях.
  • 🧩 Ошибка только на одном документе — откройте его и проверьте, заполнены ли договор, склад, организация. Иногда реквизит очищен обработкой или обменом.

Если не помогло, соберите для программиста:

  1. Полный текст ошибки вместе с модулем и строкой.
  2. Что именно нажимали: документ, кнопка, отчёт.
  3. Повторяется ли ошибка на копии базы.
  4. Номер релиза конфигурации и платформы — из окна «О программе».

С таким пакетом причину находят за минуты, а не за часы переписки.

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

Чем эта ошибка отличается от «Поле объекта не обнаружено»?
Здесь у значения вообще нет свойств: оно пустое или примитивное. «Поле объекта не обнаружено» означает, что объект есть, но свойства с таким именем у него нет — опечатка или реквизит удалён.
Почему в Конфигураторе всё работает, а у пользователя падает?
Чаще всего различаются данные: у пользователя нет записи в регистре, строки в таблице или выбран реквизит другого типа. Повторите действие на копии его базы с теми же документами.
ЗначениеЗаполнено() защищает от Неопределено и NULL?
Да, для обоих вернётся Ложь. Но и для пустой ссылки тоже — если нужно отличать «не найдено» от «найдено, но пусто», сравнивайте с Неопределено отдельно.
Можно ли включить остановку на ошибке без Конфигуратора?
Нет, отладка требует Конфигуратора или 1С:EDT и прав администратора. Пользователь может только передать полный текст ошибки и время её появления.
Ошибка появилась после обновления платформы — что делать?
Сначала проверьте, совместим ли релиз конфигурации с новой платформой: требования указаны в описании релиза. Если совместим — ищите по тексту ошибки модуль доработки или расширения.