«Индекс находится за границами диапазона»: как найти строку кода и починить цикл
Содержание
Ошибка Индекс находится за границами диапазона в 1С означает одно: код обратился к элементу массива, таблицы значений или табличной части по номеру, которого в коллекции нет. Индексы в 1С начинаются с нуля, поэтому у массива из 5 элементов последний индекс 4, а обращение Массив[5] уже вызывает сбой.
Исправляют её тремя способами. Если элементы нужно просто перебрать — меняют цикл по индексу на Для Каждого. Если элементы удаляют — идут с конца. Если берут конкретный элемент, например первую строку результата запроса, — сначала проверяют, что коллекция не пустая. Главное — найти, какая строка модуля падает. На это уходит больше всего времени.
Почему 1С выходит за границы массива
У любой индексируемой коллекции 1С есть допустимый диапазон номеров: от 0 до Количество() - 1. Метод ВГраница() у массива возвращает верхнюю границу, то есть тот же Количество() - 1. У пустого массива ВГраница() равна -1.
Платформа не проверяет индекс заранее и не подставляет пустое значение. Она прерывает выполнение и показывает окно с текстом ошибки и местом сбоя в фигурных скобках, например:
{ОбщийМодуль.ОбработкаЗаказов.Модуль(148)}: Ошибка при вызове метода контекста (Удалить)
по причине:
Индекс находится за границами диапазона
Число в скобках — номер строки модуля. Это самая полезная часть сообщения, с неё и начинается поиск.
Ошибка возникает в коллекциях, к которым обращаются по номеру:
- 📦
МассивиФиксированныйМассив—Массив[Индекс],Удалить(Индекс),Установить(Индекс, Значение); - 📊
ТаблицаЗначений—Таблица[Индекс],Таблица.Получить(Индекс); - 📄 табличные части документов и справочников —
Объект.Товары[Индекс]; - 🌳
ДеревоЗначений—Дерево.Строки[Индекс]; - 🔤 результат
СтрРазделить()— это тоже массив.
У Соответствия такой ошибки нет: при отсутствии ключа метод Получить() возвращает Неопределено. У Структуры при обращении к несуществующему ключу через точку появится другая ошибка — про поле объекта, а не про индекс.
Если ошибка появилась в типовой конфигурации без доработок, код почти наверняка не ваш. Сначала проверьте подключённые расширения и внешние обработки: в тексте ошибки будет видно, в каком модуле сбой. Если сбой в типовом модуле — обновите конфигурацию до актуального релиза или передайте текст ошибки сопровождающему программисту.
Шесть ситуаций, в которых код падает с ошибкой индекса
На практике сбой почти всегда сводится к одной из шести ситуаций. Каждую видно по коду за минуту, если знать, что искать.
1. Цикл до Количество(), а не до Количество() − 1
Классическая ошибка на единицу:
// Неправильно: последняя итерация обратится к Массив[5] при 5 элементах
Для Сч = 0 По Массив.Количество() Цикл
Сообщить(Массив[Сч]);
КонецЦикла;
Правильная граница — Массив.Количество() - 1 или Массив.ВГраница().
2. Перепутаны НомерСтроки и индекс
У строки табличной части есть реквизит НомерСтроки, и он начинается с 1. Индекс той же строки начинается с 0. Код вида Объект.Товары[Строка.НомерСтроки] для последней строки выйдет за границу, а для остальных молча возьмёт соседнюю строку. Второе даже опаснее: ошибки нет, а данные неверные.
Правильно: Объект.Товары[Строка.НомерСтроки - 1], а ещё лучше — Объект.Товары.Индекс(Строка) или работа с самой строкой без индексов.
3. Удаление элементов в прямом цикле
// Неправильно
Для Индекс = 0 По Массив.ВГраница() Цикл
Если Массив[Индекс] = Неопределено Тогда
Массив.Удалить(Индекс);
КонецЕсли;
КонецЦикла;
Граница цикла Для вычисляется один раз, при входе. После первого удаления массив короче, а цикл всё равно дойдёт до старого последнего индекса. Плюс каждый элемент после удалённого сдвигается влево, и следующий элемент пропускается без проверки.
4. Обращение к первой строке пустого результата
Таблица = Запрос.Выполнить().Выгрузить();
Цена = Таблица[0].Цена; // упадёт, если запрос ничего не вернул
Код годами работает на тестовой базе, где данные есть всегда, и падает у пользователя на новом товаре без цены.
5. Разбор строки без проверки числа частей
Части = СтрРазделить("ИНН7701234567", ";");
КПП = Части[1]; // разделителя нет, в массиве один элемент
То же бывает при разборе файлов обмена, CSV и ответов внешних сервисов: формат «почти всегда» одинаковый, но одна кривая строка роняет всю загрузку.
6. Вложенные циклы и многомерные массивы
В массиве Новый Массив(3, 4) первый индекс идёт от 0 до 2, второй — от 0 до 3. Если перепутать счётчики внешнего и внутреннего цикла, ошибка вылезет не сразу, а только когда счётчик превысит меньшую размерность. Та же беда — когда во внутреннем цикле по ошибке используют переменную внешнего.
Как найти место ошибки отладчиком 1С
Номер строки в тексте ошибки показывает, где упало, но не показывает, почему. Чтобы увидеть значения переменных в момент сбоя, нужен отладчик.
Остановка по ошибке
Это самый быстрый путь: отладчик сам остановится на строке, которая вызывает исключение.
- Откройте базу в Конфигураторе.
- Запустите отладку:
Отладка → Начать отладкуили клавишаF5. Откроется сеанс 1С:Предприятия под отладчиком. - В Конфигураторе откройте
Отладка → Остановка по ошибке…и включите остановку. Можно указать текст ошибки, напримерИндекс находится за границами диапазона, чтобы отладчик не реагировал на другие исключения. - Повторите в пользовательском режиме действие, которое вызывает ошибку.
- Конфигуратор остановится на проблемной строке и подсветит её.
Что смотреть в точке остановки
- 🔍 Значение индекса. Выделите переменную счётчика и нажмите
Shift+F9— окно «Вычислить выражение» покажет её значение. - 📏 Размер коллекции. В том же окне вычислите
Массив.Количество()илиТаблица.Количество(). Если индекс равен количеству — это ошибка на единицу, если коллекция пустая — нет проверки на пустоту. - 🧭 Стек вызовов. Покажет, через какую цепочку процедур выполнение пришло в эту строку. Полезно, когда массив собирается в одном модуле, а падает в другом.
- 📌 Табло. Добавьте туда индекс и размер коллекции, чтобы видеть их на каждом шаге без повторных вычислений.
Точка останова с условием
Если цикл проходит тысячи итераций, а сбой — на последней, обычная точка останова (F9) бесполезна: придётся нажимать «Продолжить» тысячу раз. Поставьте точку останова с условием, например Сч >= Массив.Количество() - 1. Отладчик остановится только тогда, когда условие станет истинным. Дальше пройдите код по шагам: F10 — шаг с обходом процедур, F11 — шаг с заходом в процедуру.
Отладку ведите на копии базы, а не в рабочей. Остановленный под отладчиком сеанс может держать транзакцию и блокировки, и остальные пользователи будут ждать. Если без рабочей базы не обойтись — делайте это вне рабочего времени и предупредите коллег.
Если отладчик недоступен
Бывает, что прав на Конфигуратор нет или ошибка воспроизводится только на сервере у одного пользователя. Тогда:
- Попросите пользователя в окне ошибки нажать кнопку подробностей и прислать полный текст — там будет модуль и строка.
- Найдите запись в журнале регистрации:
Администрирование → Обслуживание → Журнал регистрации, отбор по уровню «Ошибка» и времени сбоя. - Временно добавьте в код запись диагностики перед проблемной строкой:
ЗаписьЖурналаРегистрации("Диагностика.Индекс", УровеньЖурналаРегистрации.Информация, , ,
СтрШаблон("Индекс %1, элементов %2", Сч, Массив.Количество()));
После разбора диагностику уберите, чтобы не засорять журнал.
Исправление ошибки индекса: безопасный перебор и удаление
Когда место найдено, исправление обычно занимает одну-две строки. Выбор приёма зависит от того, что делает код.
Просто перебрать все элементы — Для Каждого
Если индекс не нужен, не используйте его вообще. Цикл Для Каждого не может выйти за границы:
Для Каждого Элемент Из Массив Цикл
Сообщить(Элемент);
КонецЦикла;
Для таблиц значений и табличных частей — то же самое, переменная цикла будет строкой.
Нужен номер элемента — правильная граница
Для Сч = 0 По Массив.ВГраница() Цикл
Сообщить(СтрШаблон("%1: %2", Сч + 1, Массив[Сч]));
КонецЦикла;
У пустого массива ВГраница() равна -1, и цикл от 0 до −1 не выполнится ни разу. Это корректное поведение, дополнительная проверка не нужна.
Удалить элементы — обратный цикл
Удаление с конца не сдвигает элементы, которые ещё предстоит проверить. Количество фиксируйте в переменной до начала цикла:
ВсегоЭлементов = Массив.Количество();
Для Сч = 1 По ВсегоЭлементов Цикл
Индекс = ВсегоЭлементов - Сч; // 4, 3, 2, 1, 0 при пяти элементах
Если Массив[Индекс] = Неопределено Тогда
Массив.Удалить(Индекс);
КонецЕсли;
КонецЦикла;
Тот же результат через Пока:
Индекс = Массив.ВГраница();
Пока Индекс >= 0 Цикл
Если Массив[Индекс] = Неопределено Тогда
Массив.Удалить(Индекс);
КонецЕсли;
Индекс = Индекс - 1;
КонецЦикла;
Для таблицы значений приём тот же: проходите с конца и вызывайте Таблица.Удалить(Таблица[Индекс]) или удаляйте по индексу.
Взять конкретный элемент — сначала проверка
Результат = Запрос.Выполнить();
Если Результат.Пустой() Тогда
Цена = 0;
Иначе
Цена = Результат.Выгрузить()[0].Цена;
КонецЕсли;
Для строки, разобранной через СтрРазделить, проверка выглядит так:
Части = СтрРазделить(СтрокаФайла, ";");
Если Части.Количество() < 3 Тогда
// записать в протокол загрузки и перейти к следующей строке
Продолжить;
КонецЕсли;
Артикул = Части[0];
Количество = Части[2];
Найти элемент, а не вычислять его номер
Если индекс нужен только для того, чтобы найти элемент по значению, используйте поиск коллекции. Массив.Найти(Значение) вернёт индекс или Неопределено, если значения нет. У таблицы значений есть Найти() и НайтиСтроки().
Не закрывайте ошибку конструкцией Попытка … Исключение без обработки. Ошибка пропадёт с экрана, но цикл молча пропустит данные, и итог документа или отчёта окажется неверным. Попытка уместна при разборе внешних данных, и то — с записью проблемной строки в протокол.
Сравнение способов обхода коллекций в 1С
| Способ | Когда использовать | Риск выхода за границы |
|---|---|---|
Для Каждого … Из | Перебор без изменения коллекции | Нет |
Для Сч = 0 По ВГраница() | Нужен номер элемента или параллельный проход двух массивов | Есть, если граница взята от другой коллекции |
| Обратный цикл с фиксированным количеством | Удаление элементов по условию | Нет, если количество зафиксировано до цикла |
Пока Индекс >= 0 | Удаление, сложное условие выхода | Есть, если забыть уменьшить индекс — тогда зацикливание |
Найти(), НайтиСтроки() | Поиск по значению | Нет, но результат надо проверить на Неопределено |
Соответствие вместо массива | Частый поиск по ключу | Нет, отсутствующий ключ даёт Неопределено |
Отдельно про удаление внутри Для Каждого. Ошибки индекса там обычно нет, но после удаления текущего элемента следующий сдвигается на его место, и цикл его пропускает. Данные обрабатываются не все, а сообщения об этом нет. Поэтому для удаления — только обратный цикл.
Как не допускать ошибки индекса в сложных структурах
Большая часть сбоев появляется не в простых массивах, а там, где коллекции вложены друг в друга или приходят извне.
- 🧱 Проверяйте данные на входе. Если процедура получает массив параметром и сразу берёт
Параметр[0], добавьте проверку в начало процедуры. Одна проверка на входе надёжнее десятка по коду. - 🔗 Не храните индексы между операциями. Индекс строки, запомненный до сортировки, свёртки или удаления, после них указывает на другую строку. Храните ссылку на саму строку или ключевое значение.
- 🗂 Синхронные массивы заменяйте структурами. Два массива «Товары» и «Цены» с общим индексом ломаются, как только в один добавили элемент, а в другой забыли. Массив структур или таблица значений с колонками надёжнее.
- 🌲 Дерево перебирайте рекурсивно через
Для Каждого. ОбращениеДерево.Строки[0].Строки[0]падает на первой строке без подчинённых. - 📥 JSON и XML считайте недоверенными. После чтения JSON массивы приходят с любым числом элементов, в том числе пустые. Проверяйте
Количество()перед каждым обращением по индексу. - 🧪 Тестируйте на краях. Ноль элементов, один элемент, ровно граница. Большинство ошибок индекса видно уже на пустой коллекции.
Если ошибка индекса не уходит
Бывает, что код исправлен, а сбой остался. Проверьте по порядку:
- Вы правите не тот модуль. Если ошибка в расширении, изменение в основной конфигурации не поможет, и наоборот. Сверьте имя модуля в тексте ошибки.
- Изменения не применены. После правки в Конфигураторе нужно обновить конфигурацию
базы данных (
F7), а пользователям — перезайти. Обновление структуры может потребовать монопольного доступа, поэтому делайте его, когда в базе никто не работает . - Внешняя обработка взята из старого файла. Если обработка подключена через справочник дополнительных отчётов и обработок, загрузите туда исправленный файл заново.
- Сбой в типовом коде на ваших данных. Например, у документа повреждена табличная часть после неудачного обмена. Проверьте документ на копии базы: если ошибка воспроизводится только на нём, проблема в данных, а не в алгоритме.
Частые вопросы
Чем отличается Количество() от ВГраница()?
Количество() возвращает число элементов, ВГраница() — индекс последнего элемента. У массива из 10 элементов это 10 и 9. У пустого массива — 0 и −1.Почему ошибка появляется не у всех пользователей?
Можно ли расширить массив, просто записав значение в новый индекс?
Массив[10] = Значение в массив из 10 элементов вызовет ту же ошибку. Новые элементы добавляют методом Добавить() или Вставить().