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