Обращение к несуществующему элементу коллекции в 1С 8.3: разбор ошибки и защита кода
Содержание
Ошибка «индекс находится за границами массива» в 1С означает одно: код обратился к элементу коллекции по номеру, которого в ней нет. Платформа 8.3 пишет её как Индекс находится за границами диапазона. Чаще всего виноваты три вещи: пустой результат запроса, у которого берут строку [0], цикл до Количество() вместо Количество() - 1 и удаление строк в прямом цикле.
Нумерация в коллекциях 1С начинается с нуля. В массиве из 3 элементов допустимы индексы 0, 1 и 2, а Массив[3] уже вызывает исключение. Ошибка не повреждает базу: платформа просто прерывает выполнение кода. Дальше — как найти строку, где она возникла, и как переписать код, чтобы она не повторялась.
Как платформа 1С проверяет индекс и почему выходит за границы
Каждый раз, когда код пишет Коллекция[Н], платформа сверяет Н с текущим числом элементов. Если Н меньше нуля или не меньше Количество(), выполнение останавливается с исключением. Проверка идёт в момент обращения, а не при компиляции модуля: синтаксический контроль в Конфигураторе такую ошибку не поймает.
Индексация с нуля действует для всех универсальных коллекций:
- 📦
МассивиФиксированныйМассив; - 📋
ТаблицаЗначенийи еёКолонки; - 🌳
ДеревоЗначений.Строки; - 📑
СписокЗначений; - 🧾 табличные части объектов и их аналоги в данных формы.
Граница считается так: последний допустимый индекс равен Количество() - 1. У массива есть отдельный метод ВГраница(), который сразу возвращает этот номер. Для пустого массива ВГраница() вернёт -1, и любой цикл Для Сч = 0 По Массив.ВГраница() просто не выполнится ни разу.
Соответствие и Структура по индексу не работают. Для соответствия обращение по отсутствующему ключу вернёт Неопределено, для структуры — другую ошибку, про отсутствующее поле. Если в тексте именно «за границами диапазона», ищите в строке кода квадратные скобки с числом или методы Получить, Удалить, Установить с индексом.
Почему ошибка всплывает «внезапно»
Код с выходом за границы может годами работать без сбоев. Он падает, когда данные становятся нетипичными: запрос впервые вернул ноль строк, в строке нет разделителя, у контрагента нет ни одного договора. Поэтому ошибка часто появляется после закрытия периода, загрузки данных или смены организации, хотя сам код никто не трогал.
Типовые ситуации в циклах и обработках
Ниже — сценарии, которые встречаются чаще других. В каждом приведена ошибочная запись и исправленная.
Граница цикла на единицу больше
Классика: цикл идёт от нуля до количества элементов включительно.
// Ошибка: при 5 элементах последняя итерация обращается к Массив[5]
Для Сч = 0 По Массив.Количество() Цикл
Сообщить(Массив[Сч]);
КонецЦикла;
// Правильно
Для Сч = 0 По Массив.Количество() - 1 Цикл
Сообщить(Массив[Сч]);
КонецЦикла;
Ещё надёжнее — Для Каждого Элемент Из Массив Цикл. Там индекса нет вообще, и ошибиться с границей невозможно.
Удаление элементов в прямом цикле
Граница цикла Для … По … вычисляется один раз, перед первой итерацией. Если внутри цикла удалять строки, коллекция становится короче, а счётчик продолжает идти до старого значения.
// Ошибка: после первого удаления строк меньше, чем ожидает цикл
Для Сч = 0 По Таблица.Количество() - 1 Цикл
Если Таблица[Сч].Сумма = 0 Тогда
Таблица.Удалить(Сч);
КонецЕсли;
КонецЦикла;
Правильный вариант — обходить коллекцию с конца. Тогда удаление строки не сдвигает ещё не проверенные элементы:
Всего = Таблица.Количество();
Для Сч = 1 По Всего Цикл
Индекс = Всего - Сч;
Если Таблица[Индекс].Сумма = 0 Тогда
Таблица.Удалить(Индекс);
КонецЕсли;
КонецЦикла;
При 4 строках индексы пойдут в порядке 3, 2, 1, 0. Удаление строки 3 не влияет на строки 0–2, поэтому выхода за границу нет.
Удалять строки внутри Для Каждого тоже нельзя. Исключения там обычно не будет, но цикл перескочит через элемент, следующий за удалённым. Это тихая ошибка в данных, она опаснее явного сообщения.
Разбор строки по разделителю
СтрРазделить возвращает массив. Если в строке нет разделителя, в массиве окажется один элемент, и Части[1] упадёт.
Части = СтрРазделить("ИНН7701234567", ";");
// Части.Количество() = 1, обращение к Части[1] вызовет ошибку
Если Части.Количество() > 1 Тогда
КПП = Части[1];
Иначе
КПП = "";
КонецЕсли;
Так ломаются загрузки из Excel и CSV: одна строка файла без нужного числа столбцов останавливает всю обработку.
Фиксированный номер строки табличной части
Код вида Документ.Товары[0].Номенклатура предполагает, что строка есть. Пустой документ, только что созданный пользователем, приводит к ошибке. Перед обращением проверьте Документ.Товары.Количество() > 0.
Поиск места ошибки в коде: пошагово
Начинать всегда с текста ошибки целиком. В окне сообщения нажмите Подробнее — там видно имя модуля, номер строки и саму строку кода.
- Скопируйте полный текст ошибки. Строка вида
{Обработка.ЗагрузкаПрайса.Форма.Форма.Форма(124)}означает: модуль формы обработки, строка 124. - Откройте конфигурацию в Конфигураторе:
Конфигурация → Открыть конфигурацию. - Найдите объект из текста ошибки в дереве метаданных и откройте нужный модуль.
- Перейдите к строке:
Правка → Перейти к строке(сочетаниеCtrl+G). - Найдите в этой строке обращение по индексу. Именно в нём и проблема.
Если ошибка возникает во внешней обработке или отчёте, модуль ищите в самом файле .epf или .erf: откройте его через Файл → Открыть в Конфигураторе.
Остановка в отладчике в момент ошибки
Номер строки показывает где, но не почему. Чтобы увидеть, какой был индекс и сколько элементов в коллекции, нужен отладчик.
- В Конфигураторе запустите
Отладка → Начать отладку. - Включите
Отладка → Остановка по ошибкеи отметьте остановку по всем ошибкам. - Повторите в режиме 1С:Предприятие действие, которое приводит к сбою.
- Отладчик остановится на проблемной строке. Наведите курсор на переменную индекса и на коллекцию или откройте
Табло(Shift+F9). - Сравните: индекс должен быть не больше
Количество() - 1.
Для клиент-серверной базы серверный код отлаживается только при включённой отладке на сервере 1С. Обычно для этого службу агента запускают с параметром -debug.
Перезапуск службы агента сервера 1С обрывает сеансы всех пользователей. Включайте отладку на рабочем сервере только вне рабочего времени или на копии базы.
Если доступа к Конфигуратору нет
Бухгалтер обычно видит только окно с ошибкой. В этом случае:
- 🔍 скопируйте текст через кнопку
Подробнееи передайте специалисту полностью, с модулем и номером строки; - 📜 откройте журнал регистрации:
Администрирование → Обслуживание → Журнал регистрации, отберите события с уровнем «Ошибка» за время сбоя; - 🧩 вспомните, что делали: какой документ, какая обработка, какой файл загружали.
Если в тексте упоминается расширение или внешняя обработка, а не типовой модуль, причина почти наверняка в доработке. Временно отключите расширение в разделе Администрирование → Печатные формы, отчеты и обработки и проверьте, повторяется ли сбой.
Таблица значений и результат запроса: где чаще всего выходят за границы
Самый частый источник ошибки в рабочих конфигурациях — выгрузка запроса с обращением к первой строке без проверки.
// Ошибка, если запрос не вернул ни одной строки
Договор = Запрос.Выполнить().Выгрузить()[0].Ссылка;
Запрос может вернуть пустой результат по множеству причин: другой период, удалённый элемент, не та организация в параметрах. Безопасных записей несколько:
// Вариант 1: выборка и Следующий()
Выборка = Запрос.Выполнить().Выбрать();
Если Выборка.Следующий() Тогда
Договор = Выборка.Ссылка;
Иначе
Договор = Справочники.ДоговорыКонтрагентов.ПустаяСсылка();
КонецЕсли;
// Вариант 2: проверка результата до выгрузки
Результат = Запрос.Выполнить();
Если Не Результат.Пустой() Тогда
Таблица = Результат.Выгрузить();
Договор = Таблица[0].Ссылка;
КонецЕсли;
Если нужна одна запись, добавьте в текст запроса ВЫБРАТЬ ПЕРВЫЕ 1 и упорядочивание. Так запрос быстрее, а результат предсказуем.
Колонки по номеру вместо имени
Таблица.Колонки[3] ломается, когда кто-то меняет текст запроса и полей становится меньше. Обращайтесь к колонке по имени: Таблица.Колонки.Найти("Сумма"). Метод вернёт Неопределено, если колонки нет, и это можно проверить.
Результат поиска строк
НайтиСтроки возвращает массив, который может быть пустым. Запись Таблица.НайтиСтроки(Отбор)[0] — то же обращение к первой строке без проверки. Сначала проверьте Найденные.Количество() > 0.
| Способ получить строку | Что вернёт при отсутствии данных | Риск ошибки по индексу |
|---|---|---|
Выгрузить()[0] | исключение | высокий |
Выбрать() + Следующий() | Ложь | нет |
НайтиСтроки(Отбор)[0] | исключение | высокий |
Найти(Значение, "Колонка") | Неопределено | нет |
Результат.Пустой() перед выгрузкой | Истина | нет |
Защитное программирование: как не допустить ошибку индекса
Правила, после которых ошибка практически исчезает из кода:
- ✅ обходите коллекцию через
Для Каждого, если индекс не нужен по смыслу; - ✅ для цикла по индексу верхняя граница —
Количество() - 1илиВГраница(); - ✅ удаляйте элементы обратным циклом от последнего к первому;
- ✅ перед
[0]проверяйте, что коллекция не пуста; - ✅ после
СтрРазделитьпроверяйте число частей до обращения ко второй и следующим; - ✅ к колонкам и полям обращайтесь по имени, а не по номеру.
Удобно вынести проверку в общую функцию. Она возвращает значение по умолчанию вместо исключения:
Функция ЭлементИлиПоУмолчанию(Коллекция, Индекс, ПоУмолчанию = Неопределено) Экспорт
Если Индекс >= 0 И Индекс < Коллекция.Количество() Тогда
Возврат Коллекция[Индекс];
КонецЕсли;
Возврат ПоУмолчанию;
КонецФункции
Попытка … Исключение: когда уместна
Оборачивать каждое обращение в Попытка не нужно: это скрывает реальные ошибки в данных. Используйте её на границе крупной операции, например при загрузке файла построчно. Ошибку тогда стоит записать с подробностями:
Попытка
ОбработатьСтрокуФайла(Строка);
Исключение
Текст = ПодробноеПредставлениеОшибки(ИнформацияОбОшибке());
ЗаписьЖурналаРегистрации("ЗагрузкаПрайса", УровеньЖурналаРегистрации.Ошибка, , , Текст);
КонецПопытки;
Одна строка с неправильным числом столбцов попадёт в журнал, а остальные загрузятся.
Редкие случаи и разница между версиями платформы
Сам механизм проверки индекса одинаков во всех релизах 8.3: обращение за границу всегда приводит к исключению. Различается окружение, в котором ошибка возникает.
Данные формы и серверные вызовы
В управляемой форме табличная часть доступна как ДанныеФормыКоллекция. Если индекс строки запомнили на клиенте, а затем вызвали сервер, который удалил или пересобрал строки, сохранённый номер устаревает. После серверного вызова получайте строку заново по идентификатору, через НайтиПоИдентификатору, а не по старому индексу.
Многомерные массивы
Новый Массив(2, 3) создаёт массив из 2 элементов, каждый из которых — массив из 3. Обращение Массив[1][3] выходит за границы внутреннего массива, хотя внешний индекс верный. В тексте ошибки это неочевидно: проверяйте каждый уровень отдельно.
Метод СтрРазделить и режим совместимости
СтрРазделить появилась в платформе 8.3.6. В старом коде вместо неё встречаются самописные функции разбора строки, и ошибки индекса часто сидят именно в них. Если конфигурация работает в режиме совместимости с более старой версией, новый метод может быть недоступен, и разработчик оставляет старую реализацию.
Ошибка в типовой конфигурации без доработок
Такое бывает, но редко. Если конфигурация на поддержке без изменений и расширений, а ошибка повторяется на конкретных данных:
- Уточните номер релиза:
Справка → О программеили кнопка с вопросительным знаком в правом верхнем углу. - Проверьте, не вышел ли более новый релиз с исправлением: описание изменений публикуется вместе с обновлением.
- Если обновление не помогло, запишите шаги повторения и отправьте их в поддержку.
Перед обновлением создайте резервную копию: Администрирование → Обслуживание → Резервное копирование и восстановление или выгрузкой в .dt через Конфигуратор.
Частые ошибки при исправлении
- ❌ Увеличивают размер массива
Новый Массив(100)«с запасом». Ошибка исчезает, но в коде появляются элементыНеопределено, которые ломают расчёты дальше. - ❌ Глушат исключение пустым
Исключение КонецПопытки. Пользователь не видит сбоя, а данные обрабатываются частично. - ❌ Исправляют
По Количество()наПо Количество() - 1, но оставляют удаление строк в прямом цикле. Ошибка возвращается на других данных. - ❌ Правят типовой модуль напрямую, снимая конфигурацию с поддержки. Безопаснее перенести исправление в расширение, чтобы обновления ставились без конфликтов.
Это ошибка платформы или конфигурации?
Может ли эта ошибка испортить данные в базе?
Почему ошибка есть у одного пользователя и нет у другого?
Какой индекс у последнего элемента массива?
Количество() - 1. Для массива то же значение возвращает метод ВГраница(). У пустого массива он равен -1.