Обращение через точку к пустому значению: как найти и убрать причину сбоя
Содержание
Ошибка Значение не является значением объектного типа значит одно: код обратился через точку к свойству или методу значения, у которого свойств нет. Чаще всего это Неопределено, NULL, строка или число. Исправление — найти строку из текста ошибки, понять, почему переменная оказалась пустой, и поставить проверку до обращения через точку.
Полный текст обычно выглядит так: {ОбщийМодуль.ПродажиСервер.Модуль(214)}: Значение не является значением объектного типа (Контрагент). В фигурных скобках — модуль и номер строки, в круглых — имя свойства, к которому не удалось обратиться. Эти две подсказки уже дают половину решения. Поведение одинаково для всех конфигураций на платформе 1С 8.3: это ошибка встроенного языка, а не конкретной программы.
Почему 1С не может прочитать свойство: какие типы считаются объектными
Во встроенном языке у одних значений есть свойства и методы, у других нет. Обращение Переменная.Что-то работает только для первых.
| Значение | Есть свойства через точку | Пример |
|---|---|---|
Ссылка (СправочникСсылка, ДокументСсылка) | ✅ да, реквизиты объекта | Договор.Владелец |
Объект (СправочникОбъект, ДокументОбъект) | ✅ да | ДокОбъект.Дата |
| Структура, строка таблицы значений, выборка | ✅ да, поля и колонки | Выборка.Сумма |
Неопределено | ❌ нет | — |
NULL | ❌ нет | — |
| Строка, Число, Дата, Булево | ❌ нет | — |
Когда в переменной оказывается значение из нижней части таблицы, платформа останавливает выполнение и показывает эту ошибку. В английском интерфейсе текст звучит как Value is not of object type.
💡 Пустая ссылка вроде Справочники.Контрагенты.ПустаяСсылка() — это всё ещё значение ссылочного типа. Обращение к её реквизиту возвращает пустое значение, а не эту ошибку. Поэтому проверка ЗначениеЗаполнено() и проверка на Неопределено решают разные задачи — об этом ниже.
Типичные ситуации, в которых возникает ошибка объектного типа
Почти все случаи сводятся к одной схеме: программист рассчитывал на объект, а получил пустоту. Вот откуда она берётся.
Поиск, который ничего не нашёл
Методы поиска в коллекциях возвращают Неопределено, если подходящего элемента нет:
- 🔍
ТаблицаЗначений.Найти()иДеревоЗначений.Строки.Найти(); - 🔍
Массив.Найти()возвращаетНеопределеновместо индекса; - 🔍
Соответствие.Получить()при отсутствии ключа.
Код Товары.Найти(Номенклатура, "Номенклатура").Количество упадёт ровно в тот момент, когда номенклатуры в таблице не окажется. На тестовых данных всё работало, у пользователя — нет.
NULL из запроса
При левом соединении поля из правой таблицы получают NULL, если связанной записи нет. Выборка Выборка.Склад.Наименование даст ошибку для каждой строки без склада. Классический пример — остатки, соединённые со справочником, или документ без строки в регистре сведений.
Реквизит составного типа
Если реквизит может хранить и ссылку, и строку, то при строковом значении обращение Реквизит.Наименование сломается. Так часто бывает с полем «Субконто» или с реквизитами, где допустимы «Справочник.Контрагенты» и «Строка».
Функция вернула Неопределено
Функция, у которой есть ветка без Возврат, отдаёт Неопределено. То же с ПолучитьИзВременногоХранилища() по устаревшему адресу и с параметрами, которые не передали при вызове.
Табличная часть без выбранной строки
В управляемой форме Элементы.Товары.ТекущиеДанные равно Неопределено, когда строка не выделена или таблица пустая. Подробно — в разделе про формы.
Как найти причину ошибки отладчиком
Номер строки из текста ошибки показывает, где упало. Отладчик показывает, почему.
Включить остановку по ошибке
- Откройте базу в Конфигураторе.
- Запустите отладку:
Отладка → Начать отладкуи выберите тонкий клиент. - В Конфигураторе включите
Отладка → Остановка по ошибке. Флажок можно оставить для всех ошибок или задать текстЗначение не является значением объектного типа, чтобы не останавливаться на остальных. - Повторите в клиенте действие, которое вызывало сбой.
Выполнение остановится прямо на проблемной строке. Не придётся искать модуль вручную.
Посмотреть, что лежит в переменной
На строке остановки выделите выражение до точки и нажмите Shift+F9 — откроется окно вычисления выражения. Полезно проверить три вещи:
Переменная— что в ней сейчас;ТипЗнч(Переменная)— какой тип пришёл на самом деле;Переменная = Неопределено— простая проверка на пустоту.
Цепочку вроде Документ.Договор.Владелец.ИНН разбирайте по звеньям. Ошибка относится к последнему звену перед свойством из скобок. Если в скобках ИНН, пустым оказался Владелец.
Отладка на сервере
В файловой базе серверный код отлаживается сразу. В клиент-серверной базе отладка на сервере работает, только если служба агента сервера запущена с ключом -debug. Добавление ключа требует перезапуска службы.
Перезапуск службы 1С оборвёт сеансы всех пользователей кластера. Делайте это вне рабочего времени или на копии базы.
💡 Удобнее всего повторять сбой на копии рабочей базы. Тогда можно останавливаться на точках сколько угодно, не мешая бухгалтерии.
Журнал регистрации и технологический журнал при ошибке объектного типа
Если ошибка возникла у пользователя вчера, а воспроизвести её не получается, нужны следы.
Журнал регистрации
В БП 3.0, ЗУП 3.1 и УТ 11 журнал открывается через Администрирование → Обслуживание → Журнал регистрации. В Конфигураторе — через Администрирование → Журнал регистрации.
Что настроить в отборе:
- Уровень — «Ошибка».
- Период — время, когда пользователь видел сообщение.
- Пользователь — тот, у кого возникла ошибка.
В комментарии записи будет полный текст с модулем и номером строки. По событию видно, что делал пользователь: проводил документ, открывал форму, запускал регламентное задание. Последнее особенно ценно: ошибки фоновых заданий пользователь вообще не видит.
Технологический журнал
Когда журнала регистрации мало, включите технологический журнал. Это файл 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
| Договоры.Ссылка КАК Ссылка
|ИЗ
| Справочник.ДоговорыКонтрагентов КАК Договоры
|ГДЕ
| Договоры.Владелец = &Контрагент
| И НЕ Договоры.ПометкаУдаления";
Запрос.УстановитьПараметр("Контрагент", Контрагент);
Выборка = Запрос.Выполнить().Выбрать();
Если Выборка.Следующий() Тогда
Возврат Выборка.Ссылка;
КонецЕсли;
Возврат Справочники.ДоговорыКонтрагентов.ПустаяСсылка();
КонецФункции
Пустая ссылка безопаснее Неопределено: обращение к её реквизитам не уронит код, а ЗначениеЗаполнено() покажет, что договора нет.
Не оборачивайте в Попытку свой собственный код вместо проверки на Неопределено — так ошибка превращается в тихо испорченные данные.
Если ошибка появилась в типовой конфигурации
Пользователю без навыков программиста не нужно лезть в код. Сначала определите по тексту ошибки, чей модуль упал.
- 🧩 В тексте имя внешней обработки или отчёта — сбой в подключённой доработке. Найдите её в
Администрирование → Печатные формы, отчеты и обработкии временно отключите. - 🧩 В тексте модуль расширения — отключите расширение в том же разделе и проверьте, ушла ли ошибка.
- 🧩 Модуль типовой конфигурации — проверьте, установлен ли актуальный релиз. Подобные сбои разработчики типовых решений регулярно исправляют в обновлениях.
- 🧩 Ошибка только на одном документе — откройте его и проверьте, заполнены ли договор, склад, организация. Иногда реквизит очищен обработкой или обменом.
Если не помогло, соберите для программиста:
- Полный текст ошибки вместе с модулем и строкой.
- Что именно нажимали: документ, кнопка, отчёт.
- Повторяется ли ошибка на копии базы.
- Номер релиза конфигурации и платформы — из окна «О программе».
С таким пакетом причину находят за минуты, а не за часы переписки.