«Индекс находится за границами диапазона»: где искать ошибку в коде 1С и как её закрыть
Содержание
Ошибка индекс за границей массива в 1С означает одно: код обратился к элементу коллекции по номеру, которого в ней нет. Платформа 1С 8.3 показывает это как Индекс находится за границами диапазона и останавливает выполнение модуля. Индексы в массивах, таблицах значений и списках считаются с нуля, поэтому у массива из 5 элементов последний индекс 4, а обращение к Массив[5] уже ошибка.
Исправляют её по-разному в зависимости от того, кто вы. Бухгалтеру в типовой конфигурации нужно собрать текст ошибки и передать его разработчику или обновить релиз
. Программисту — найти строку через отладчик и поправить границы цикла или добавить проверку количества элементов. Обходить ошибку конструкцией Попытка без исправления причины — худший вариант.
Как платформа 1С проверяет индекс и когда выбрасывает исключение
Любая индексированная коллекция в 1С хранит количество элементов. При обращении Коллекция[N] платформа сравнивает N с этим количеством. Если N меньше нуля или больше либо равен количеству, выполнение прерывается исключением.
Что важно знать о механике:
- 🔢 Нумерация с нуля. Допустимые индексы — от
0доКоличество() - 1. У пустой коллекции допустимых индексов нет вообще. - 📏 Проверка в момент обращения. Ошибка возникает не при создании массива, а в той строке, где идёт чтение или запись по индексу.
- 🧱 Одинаково для разных типов. Тот же текст ошибки дают
Массив,ФиксированныйМассив,ТаблицаЗначений,СписокЗначений, табличные части документов и коллекции данных формы. - 🧭 Методы с индексом тоже проверяют границы.
Удалить(Индекс),Установить(Индекс, Значение),Получить(Индекс)с неверным номером дают ту же ошибку.
Исключение необработанное: если в стеке вызовов нет Попытка, пользователь видит окно с ошибкой, а текущая операция (проведение, запись, формирование отчёта) отменяется. Если код выполнялся в транзакции, транзакция откатывается целиком.
Табличная часть документа — частая ловушка. Поле НомерСтроки начинается с 1, а индекс строки — с 0. Строка с НомерСтроки = 3 лежит по индексу 2.
Типичные сценарии: где в циклах и обращениях возникает выход за границу
Ниже — ситуации, которые дают эту ошибку на практике. Все примеры на встроенном языке 1С 8.3.
1. Граница цикла на единицу больше
Самый частый случай. Разработчик пишет верхнюю границу как количество элементов:
// Ошибка: на последнем шаге Индекс = Количество()
Для Индекс = 0 По Товары.Количество() Цикл
Сумма = Сумма + Товары[Индекс].Сумма;
КонецЦикла;
Правильно — Количество() - 1. Для пустой коллекции граница станет -1, и цикл просто не выполнится ни разу, без ошибки.
Для Индекс = 0 По Товары.Количество() - 1 Цикл
Сумма = Сумма + Товары[Индекс].Сумма;
КонецЦикла;
Если индекс в теле не нужен, используйте Для Каждого Строка Из Товары Цикл — там выйти за границу нельзя.
2. Удаление элементов в прямом цикле
В 1С выражение верхней границы цикла Для ... По вычисляется один раз, до первого шага. Если внутри цикла удалять элементы, коллекция укорачивается, а граница остаётся прежней. На последних шагах индекс выходит за пределы.
// Ошибка: было 5 элементов, после удаления их меньше, а цикл идёт до 4
Для Индекс = 0 По Массив.Количество() - 1 Цикл
Если Массив[Индекс] = 0 Тогда
Массив.Удалить(Индекс);
КонецЕсли;
КонецЦикла;
Кроме ошибки, такой цикл пропускает элементы: после удаления следующий элемент сдвигается на место удалённого. Как переписать правильно — в разделе про работу с коллекциями.
3. Обращение к первому элементу пустого результата
Код ожидает «хотя бы одну строку» и берёт её сразу:
- 📄
РезультатЗапроса.Выгрузить()[0]— запрос ничего не нашёл, таблица пустая. - 🔍
ТЗ.НайтиСтроки(Отбор)[0]— подходящих строк нет, возвращается пустой массив. - ✂️
СтрРазделить(Строка, ";")[2]— во входной строке меньше трёх частей.
Такие ошибки «плавающие»: в одной базе работают годами, в другой падают на первом же документе без нужных данных.
4. Пакет запросов с неверным номером результата
Запрос.ВыполнитьПакет() возвращает массив результатов по каждому запросу пакета, включая запросы, которые помещают данные во временные таблицы. Добавили в начало пакета ещё один запрос ПОМЕСТИТЬ — и все номера сдвинулись. Обращение Результаты[3] при четырёх запросах в пакете ещё работает, при трёх — уже ошибка.
5. Индекс, сохранённый «на потом»
Номер строки запомнили, а коллекцию потом изменили: отсортировали, удалили строку, перезаполнили. На клиенте это частая история с табличной частью формы: индекс получили до серверного вызова, а после него строк стало меньше.
Как найти строку кода, где возникает ошибка индекса
Начинать нужно с подробного текста ошибки. В нём платформа указывает модуль и номер строки, например {Документ.РеализацияТоваровУслуг.МодульОбъекта(214)}.
Шаги для пользователя типовой конфигурации
- В окне ошибки откройте подробный текст и скопируйте его целиком, вместе со стеком вызовов.
- Посмотрите имя модуля.
ВнешняяОбработкаилиВнешнийОтчет— проблема во внешней обработке. Имя расширения в стеке — проблема в расширении конфигурации. - Откройте журнал регистрации: в БП 3.0 и ЗУП 3.1 это
Администрирование → Обслуживание, ссылка «Журнал регистрации». Отберите события с уровнем «Ошибка» за время сбоя. - Передайте текст и стек тому, кто сопровождает базу. Если ошибка в типовом модуле без доработок, проверьте, установлен ли актуальный релиз: такие ошибки обычно исправляют в обновлениях.
Не правьте код типовой конфигурации на рабочей базе без резервной копии. Снятие конфигурации с поддержки усложняет все будущие обновления. Доработки делайте в расширении.
Отладка в Конфигураторе
Программист находит причину за несколько минут:
- Откройте базу в Конфигураторе и запустите отладку: меню
Отладка → Начать отладку(клавишаF5). - Включите остановку по ошибке: меню
Отладка → Остановка по ошибке. Отладчик остановится прямо на строке с исключением. - Повторите действие, которое вызывает сбой.
- В точке остановки откройте вычисление выражения (
Shift+F9) и посмотрите значения индекса иКоллекция.Количество(). - Если ошибка в цикле на тысячах строк, поставьте точку останова с условием, например
Индекс >= Товары.Количество() - 1, чтобы не листать шаги вручную.
Пошаговое выполнение: F10 — шаг с обходом процедуры, F11 — шаг с заходом внутрь. Стек вызовов покажет, откуда пришли неверные данные.
Для кода, который выполняется на сервере 1С:Предприятия (клиент-серверный вариант), на сервере должна быть разрешена отладка. Без этого отладчик остановится только в клиентском коде.
Безопасная работа с массивами и коллекциями: как избежать выхода за границу
Правила, которые закрывают почти все сценарии из списка выше.
Проверяйте количество перед обращением по индексу.
Строки = ТЗ.НайтиСтроки(Новый Структура("Номенклатура", Товар));
Если Строки.Количество() > 0 Тогда
Цена = Строки[0].Цена;
Иначе
Цена = 0;
КонецЕсли;
Удаляйте с конца. Фиксируем количество в переменной до цикла, идём от последнего индекса к нулевому. Удаление элемента сдвигает только те, что правее, а их мы уже прошли.
Количество = Массив.Количество();
Для Сч = 1 По Количество Цикл
Индекс = Количество - Сч;
Если Массив[Индекс] = 0 Тогда
Массив.Удалить(Индекс);
КонецЕсли;
КонецЦикла;
Проверка на массиве из 5 элементов: индексы идут 4, 3, 2, 1, 0 — все в допустимых границах при любом числе удалений.
Не обращайтесь к фиксированному индексу результата разбора строки.
Части = СтрРазделить(СтрокаИмпорта, ";");
Если Части.Количество() < 3 Тогда
ВызватьИсключение СтрШаблон("Ожидалось 3 поля, получено %1", Части.Количество());
КонецЕсли;
Код = Части[0];
Наименование = Части[1];
Цена = Части[2];
Номера результатов пакета держите рядом с текстом запроса. Заведите переменную с номером, например НомерРезультатаОстатки = 2, и комментируйте, какой запрос ей соответствует. Ещё надёжнее — выносить нужный результат в последний запрос пакета и брать его как Результаты[Результаты.Количество() - 1].
| Подход | Когда применять | Риск |
|---|---|---|
Для Каждого ... Из | Нужен только перебор, без удаления | Нет выхода за границу, но удалять внутри нельзя |
Для Индекс = 0 По Количество() - 1 | Нужен номер элемента | Ошибка, если коллекция меняется в цикле |
| Обратный цикл с зафиксированным количеством | Удаление элементов по условию | Нужно аккуратно считать индекс |
НайтиСтроки + удаление по строкам | Таблица значений, удаление по отбору | Нужна проверка, что строки найдены |
Попытка-Исключение: когда она уместна при ошибке индекса
Попытка ... Исключение ... КонецПопытки перехватывает исключение и даёт коду продолжить работу. Для выхода за границы массива это почти всегда маскировка ошибки, а не исправление. Если индекс может оказаться неверным, это проверяется через Количество() до обращения.
Перехват оправдан в других случаях:
- 📥 загрузка внешних данных, где битая строка не должна останавливать обработку всего файла;
- 🔁 регламентные и фоновые задания, где ошибку нужно записать и перейти к следующему объекту;
- 🧪 вызов чужого кода, который вы не можете исправить сразу.
Пример с записью в журнал регистрации:
Для Каждого СтрокаФайла Из СтрокиФайла Цикл
Попытка
ЗагрузитьСтроку(СтрокаФайла);
Исключение
ТекстОшибки = ПодробноеПредставлениеОшибки(ИнформацияОбОшибке());
ЗаписьЖурналаРегистрации("Загрузка прайса",
УровеньЖурналаРегистрации.Ошибка, , , ТекстОшибки);
КонецПопытки;
КонецЦикла;
Внутри транзакции перехват не спасает. После исключения транзакция помечается как ошибочная, и при дальнейшей записи платформа выдаст В данной транзакции уже происходили ошибки. Транзакцию нужно откатить и, при необходимости, вызвать исключение повторно через ВызватьИсключение.
Пустой блок Исключение КонецПопытки без записи в журнал — запрет. Ошибка пропадёт из вида, а данные останутся неполными: например, в документ не попадёт строка, и никто не узнает почему.
Таблицы значений, списки значений и табличные части: что учесть
У каждого типа коллекции свои способы обойтись без индекса вовсе.
Таблица значений
- 🔎 Для поиска строки используйте
Найти(Значение, "Колонка")— он возвращает строку илиНеопределено, а не падает. - 📋
НайтиСтроки(Структура)возвращает массив; перед[0]проверяйте его количество. - 🗑️ Удаляйте найденные строки по ссылке на строку:
ТЗ.Удалить(Строка). Перебор идёт по массиву найденных строк, а не по самой таблице, поэтому сдвиг индексов не мешает. - 🔢 Индекс известной строки даёт метод
ТЗ.Индекс(Строка). Не вычисляйте его по номеру строки или порядку добавления.
УдаляемыеСтроки = ТЗ.НайтиСтроки(Новый Структура("Количество", 0));
Для Каждого Строка Из УдаляемыеСтроки Цикл
ТЗ.Удалить(Строка);
КонецЦикла;
Колонки тоже коллекция: ТЗ.Колонки[5] при пяти колонках вызовет ту же ошибку. Обращайтесь к колонкам по имени.
Список значений
- 🔎
НайтиПоЗначению(Значение)возвращает элемент илиНеопределено. - 🧷
Индекс(Элемент)даёт номер элемента; по нему безопасно вызыватьУдалить. - ⚠️ Если список заполнили на сервере и передали на клиент, порядок элементов сохраняется, но после
СортироватьПоЗначению()илиСортироватьПоПредставлению()прежние индексы уже другие.
Табличная часть документа и формы
- 🔢
Объект.Товары[Индекс]использует индекс с нуля;НомерСтроки— с единицы. - 🆔 В коллекции данных формы строку надёжнее искать по идентификатору:
ПолучитьИдентификатор()у строки иНайтиПоИдентификатору(Идентификатор)у коллекции. Идентификатор не меняется при удалении других строк, в отличие от индекса. - 👆 Текущую строку берите через свойство
ТекущиеДанныеэлемента формы и проверяйте наНеопределено: пользователь мог не выбрать ни одной строки.
ТекущиеДанные = Элементы.Товары.ТекущиеДанные;
Если ТекущиеДанные = Неопределено Тогда
Возврат;
КонецЕсли;
Как не допустить ошибку индекса в новом коде
Несколько привычек, которые снижают число таких сбоев:
- ✅ Перебор без индекса по умолчанию.
Для Каждоговезде, где номер элемента не нужен. - ✅ Граница цикла только
Количество() - 1. Одна запись, без вариантов. - ✅ Любое
[0]— после проверки количества. Особенно для результатов запросов иНайтиСтроки. - ✅ Тест на пустых данных. Перед сдачей доработки проведите документ с пустой табличной частью и сформируйте отчёт за период без движений.
- ✅ Тест на граничных размерах. Одна строка, две строки, строка с последним индексом. Ошибка «на единицу» проявляется именно здесь.
- ✅ Проверка конфигурации. В Конфигураторе запускайте
Конфигурация → Проверка конфигурациис расширенной проверкой. Выход за границы она не ловит, но находит смежные проблемы: недоступные переменные, вызовы несуществующих методов.
Если доработок много, полезно договориться в команде о единой схеме удаления элементов из коллекций и проверять её на код-ревью. Ошибки этого типа однообразны, и их легко заметить глазами.
Частые вопросы
Чем отличается эта ошибка от «Поле объекта не обнаружено»?
Может ли ошибка возникнуть из-за данных, а не из-за кода?
Помогут ли тестирование и исправление информационной базы?
Почему ошибка есть у меня, а у коллеги с тем же релизом нет?
Можно ли создать массив нужного размера заранее?
Новый Массив(10) создаст массив из 10 элементов со значением Неопределено, индексы от 0 до 9. Обращение [10] всё равно даст ошибку.