Откуда берётся «Индекс находится за границами диапазона» и как найти строку кода с ошибкой
Содержание
Ошибка «индекс за пределами массива» в 1С возникает, когда код обращается к элементу коллекции по номеру, которого нет: к Массив[5] при пяти элементах (их индексы 0–4), к Товары[0] в пустой табличной части. Чтобы исправить, нужно найти модуль и строку из текста ошибки и добавить проверку количества элементов перед обращением или переписать цикл.
В 1С 8.3 сообщение звучит так: Индекс находится за границами диапазона. Обычно перед ним стоит адрес в фигурных скобках, например {ОбщийМодуль.ОбменСБанком.Модуль(214)}. Это и есть главная подсказка: имя модуля и номер строки. Бухгалтер видит такое окно при проведении документа или формировании отчёта, а программист по этому адресу за пять минут находит виноватый код.
Почему 1С выходит за границы массива: пять причин в коде
Индексация всех коллекций в 1С начинается с нуля. У массива из 10 элементов первый индекс 0, последний 9. Метод ВГраница() вернёт 9, Количество() вернёт 10. Почти все ошибки индекса растут из путаницы этих двух чисел или из предположения «тут точно что-то есть».
Что встречается на практике:
- 🔢 Цикл до
Количество()вместоВГраница().Для Индекс = 0 По Массив.Количество() Циклделает на один шаг больше, чем элементов. - 🗑️ Удаление элементов в прямом цикле. Граница цикла
Длявычисляется один раз при входе. После каждогоУдалить()коллекция короче, а счётчик идёт до старой границы. - 📭 Обращение к пустой коллекции.
Объект.Товары[0],Результат.Выгрузить()[0],Элементы.Список.ВыделенныеСтроки[0], когда пользователь ничего не выделил. - ✂️ Разбор строки на части.
СтрРазделить("Иванов;Иван", ";")вернёт 2 элемента. Обращение к[2]за отчеством упадёт. - 📄 Внешние данные другой структуры. В загружаемом файле Excel или CSV колонок меньше, чем ждёт код. Банк поменял формат выписки, и обработка обращается к колонке, которой больше нет.
Те же правила действуют для таблицы значений, табличной части, списка значений, колонок таблицы (ТЗ.Колонки[5]) и коллекций данных формы. Текст ошибки одинаковый, лечится одинаково.
Отдельно отметим, чего эта ошибка не касается. Соответствие по несуществующему ключу через квадратные скобки вернёт Неопределено, а не ошибку индекса. У Структуры при отсутствии ключа будет другое сообщение — Поле объекта не обнаружено.
Как найти строку с ошибкой индекса: адрес, стек и отладчик
Шаг 1. Прочитайте адрес в тексте ошибки
В окне ошибки нажмите ссылку подробностей. Появится стек вызовов: список модулей сверху вниз с номерами строк. Верхняя строка — место, где код упал. Нижние — кто его вызвал.
Разбор адреса {Документ.РеализацияТоваровУслуг.МодульОбъекта(1187)}:
| Часть | Что означает |
|---|---|
Документ.РеализацияТоваровУслуг | Объект метаданных |
МодульОбъекта | Какой модуль: объекта, менеджера, формы, общий |
1187 | Номер строки в модуле |
Если в адресе стоит имя расширения или внешней обработки, проблема не в типовом коде. Это сразу сужает поиск.
Шаг 2. Откройте модуль в конфигураторе
- Запустите конфигуратор, откройте
Конфигурация → Открыть конфигурацию. - Найдите объект в дереве метаданных и откройте нужный модуль.
- Нажмите
Ctrl+Gи введите номер строки из адреса.
Строка с квадратными скобками и есть кандидат. Если в ней несколько обращений по индексу, понадобится отладчик.
Шаг 3. Поймайте момент ошибки отладчиком
- В конфигураторе включите
Отладка → Остановка по ошибкеи отметьте флажок остановки. В поле фильтра можно вписать часть текста:границами диапазона— тогда отладчик не будет останавливаться на других исключениях. - Запустите
Отладка → Начать отладку(F5). - Повторите действие пользователя: проведите тот же документ, сформируйте тот же отчёт.
- Отладчик остановится на строке ошибки. Выделите имя коллекции и нажмите
Shift+F9— увидите её размер и содержимое. - Откройте
Отладка → Стек вызовов, чтобы понять, откуда пришли данные.
Обычно картина становится ясна сразу: массив пустой или в нём на один элемент меньше, чем ждёт код.
Код на сервере в клиент-серверном варианте отлаживается, только если на сервере 1С включена отладка (параметр -debug у службы агента сервера). Включение требует перезапуска службы, а он обрывает сеансы всех пользователей. Делайте это вне рабочего времени или на тестовом сервере.
Ошибка воспроизводится только у одного пользователя? Значит, дело в его данных: настройках отчёта, выделенных строках, конкретном документе. Отлаживайте под его учётной записью или на копии базы с теми же данными.
Циклы и коллекции без выхода за границу массива
Большинство случаев исправляется переписыванием цикла. Ниже три шаблона, которые закрывают типовые ситуации.
Перебор без изменения коллекции
Если индекс не нужен, используйте Для Каждого. Выйти за границу в нём невозможно:
Для Каждого Строка Из Объект.Товары Цикл
Итого = Итого + Строка.Сумма;
КонецЦикла;
Если индекс нужен, граница — ВГраница():
Для Индекс = 0 По Массив.ВГраница() Цикл
Сообщить(Массив[Индекс]);
КонецЦикла;
У пустого массива ВГраница() возвращает −1, и тело цикла не выполнится ни разу. Для табличных частей и таблиц значений аналога ВГраница() нет, там пишут По Коллекция.Количество() - 1.
Удаление элементов: только с конца
Ошибочный вариант. Граница посчитана один раз, а массив укорачивается:
// Массив = [0, 5, 0, 7]; ВГраница() = 3
Для Индекс = 0 По Массив.ВГраница() Цикл
Если Массив[Индекс] = 0 Тогда
Массив.Удалить(Индекс);
КонецЕсли;
КонецЦикла;
Прогоним в уме. Индекс 0: удалили ноль, массив стал [5, 0, 7]. Индекс 1: удалили ноль, массив [5, 7]. Индекс 2: элемента нет — ошибка. Заодно видно и вторую беду: прямой цикл перескакивает через элемент, стоящий сразу за удалённым.
Правильный вариант — обход с конца с зафиксированным размером:
ВсегоЭлементов = Массив.Количество();
Для Сч = 1 По ВсегоЭлементов Цикл
Индекс = ВсегоЭлементов - Сч;
Если Массив[Индекс] = 0 Тогда
Массив.Удалить(Индекс);
КонецЕсли;
КонецЦикла;
Индексы идут 3, 2, 1, 0. Удаление сдвигает только элементы правее текущего, а их мы уже проверили. Для табличной части схема та же: Объект.Товары.Удалить(Индекс).
Сбор в новый массив вместо удаления
Ещё надёжнее вообще не удалять, а собирать подходящие элементы в новую коллекцию:
Отобранные = Новый Массив;
Для Каждого Значение Из Массив Цикл
Если Значение <> 0 Тогда
Отобранные.Добавить(Значение);
КонецЕсли;
КонецЦикла;
Массив = Отобранные;
Для таблиц значений тот же эффект даёт НайтиСтроки() с отбором или копирование нужных строк.
| Подход | Когда применять | Риск |
|---|---|---|
Для Каждого | Чтение, расчёт итогов | Нельзя удалять элементы внутри |
Для … По ВГраница() | Нужен индекс, коллекция не меняется | Ошибка, если внутри удаляют |
| Обратный цикл | Удаление по условию | Нужно зафиксировать размер до цикла |
| Новая коллекция | Фильтрация больших массивов | Дополнительная память |
Типовые места ошибки индекса и как их исправить
Первая строка табличной части или выборки
Код вида Объект.Товары[0].Номенклатура работает, пока в документе есть строки. Пустой документ его роняет. Исправление:
Если Объект.Товары.Количество() > 0 Тогда
ПерваяНоменклатура = Объект.Товары[0].Номенклатура;
КонецЕсли;
С запросом то же самое. Вместо Запрос.Выполнить().Выгрузить()[0].Сумма используйте выборку:
Выборка = Запрос.Выполнить().Выбрать();
Сумма = 0;
Если Выборка.Следующий() Тогда
Сумма = Выборка.Сумма;
КонецЕсли;
Метод Следующий() вернёт Ложь на пустом результате, и ошибки не будет.
Выделенные строки в списке
Команда формы, которая берёт Элементы.Список.ВыделенныеСтроки[0], падает, если пользователь нажал кнопку без выделения. Проверка:
Выделенные = Элементы.Список.ВыделенныеСтроки;
Если Выделенные.Количество() = 0 Тогда
ПоказатьПредупреждение(, "Выделите строку в списке.");
Возврат;
КонецЕсли;
Разбор строки и загрузка файлов
Для частей строки проверяйте количество до обращения:
Части = СтрРазделить(ФИО, " ");
Фамилия = ?(Части.Количество() > 0, Части[0], "");
Имя = ?(Части.Количество() > 1, Части[1], "");
Отчество = ?(Части.Количество() > 2, Части[2], "");
При загрузке файла проверяйте структуру один раз в начале, а не на каждой строке. Если колонок меньше ожидаемого, выводите понятное сообщение: «В файле 6 колонок, ожидается 8. Проверьте формат выписки». Это лучше, чем падение на сотой строке.
Пустая строка в конце CSV-файла — частая причина. СтрРазделить вернёт для неё массив из одного пустого элемента. Пропускайте строки, у которых СокрЛП(Строка) = "".
Попытка–Исключение: когда ловить ошибку индекса, а когда нет
Конструкция Попытка … Исключение перехватывает ошибку и не даёт ей прервать выполнение. Соблазн обернуть проблемную строку велик, но это лечение симптома.
Когда исключение уместно:
- ✅ обработка внешних данных, где формат не контролируется: файлы, ответы веб-сервисов;
- ✅ пакетная обработка тысяч документов, где одна сбойная запись не должна останавливать остальные;
- ✅ регламентные задания, которые должны записать ошибку и продолжить.
Когда нельзя:
- ❌ вместо проверки
Количество()в обычном коде; - ❌ с пустым блоком
Исключение, который молча глотает ошибку; - ❌ внутри транзакции записи документа, если после ошибки код продолжает писать данные.
Правильный шаблон с записью в журнал регистрации:
Попытка
ОбработатьСтрокуФайла(Части);
Исключение
ТекстОшибки = ПодробноеПредставлениеОшибки(ИнформацияОбОшибке());
ЗаписьЖурналаРегистрации("ЗагрузкаВыписки",
УровеньЖурналаРегистрации.Ошибка, , , ТекстОшибки);
КоличествоОшибок = КоличествоОшибок + 1;
КонецПопытки;
Пустой блок Исключение превращает понятную ошибку в неверные данные, которые находят через месяц. Если ошибку нельзя обработать осмысленно, пусть она дойдёт до пользователя. Для явного прерывания с понятным текстом используйте ВызватьИсключение "Не заполнена табличная часть Товары".
Если исключение возникло внутри транзакции, транзакция помечается к отмене. Продолжить запись в ней не получится: код упадёт уже с другой ошибкой при фиксации.
Ошибка индекса после обновления конфигурации
Документ проводился год, а после обновления релиза падает. Это отдельный сценарий, и разбирать его нужно по адресу из ошибки.
Адрес указывает на расширение или внешнюю обработку. Самый частый вариант. Доработка писалась под старую структуру: в типовой табличной части поменялись колонки, процедура стала возвращать массив другой длины. Что сделать:
- Откройте
Администрирование → Печатные формы, отчёты и обработки → Расширения. - Отключите подозрительное расширение и проверьте операцию снова.
- Если ошибка ушла, передайте разработчику расширения текст ошибки и номер нового релиза.
Внешние печатные формы и обработки там же, в разделе Дополнительные отчёты и обработки. Временно отключите нужную и проверьте.
Адрес указывает на типовой модуль. Возможны два случая. Конфигурация снята с поддержки и при обновлении объединялась с доработками — тогда виновато неудачное объединение, ищите доработанный фрагмент рядом со строкой ошибки. Конфигурация на полной поддержке — тогда это ошибка релиза: проверьте, не вышел ли следующий, и сообщите в поддержку фирмы-разработчика с текстом ошибки.
Как снизить риск на будущее:
- 🧪 обновляйте сначала копию базы и проверяйте ключевые операции: проведение основных документов, закрытие месяца, загрузку выписок;
- 📌 держите список расширений и внешних обработок с указанием, под какой релиз они проверены;
- 🛡️ при доработках обращайтесь к коллекциям через проверки
Количество(), а не через допущение «строка всегда есть»; - 📋 смотрите журнал регистрации после обновления:
Администрирование → Обслуживание → Журнал регистрации, отбор по уровню «Ошибка».