Выход за границы диапазона в коде 1С: где искать строку и как переписать обращение

· 10 мин

Содержание

Ошибка «индекс находится за границами массива» в 1С означает одно: код обратился к элементу коллекции по номеру, которого в ней нет. Например, в массиве три элемента с индексами 0, 1 и 2, а программа просит Массив[3]. Платформа 1С 8.3 выводит это сообщение как Индекс находится за границами диапазона — слово «массив» в нём не встречается, хотя чаще всего речь именно о массиве.

Частое заблуждение — считать, что «испортилась база» и пора запускать тестирование и исправление. Почти всегда виноват код: типовой модуль, расширение, внешняя обработка или печатная форма. Данные лишь провоцируют сбой — например, пустая табличная часть, которую код считал заполненной.

Откуда берётся выход за границы коллекции

В 1С индексация начинается с нуля. Последний элемент коллекции из N элементов имеет индекс N - 1. Всё, что меньше нуля или больше этой границы, вызывает ошибку.

Индексом можно обратиться к разным объектам:

  • 📦 Массив и ФиксированныйМассив;
  • 📋 ТаблицаЗначений и её строке (по номеру колонки);
  • 🧾 табличной части объекта и коллекции данных формы;
  • 📑 СписокЗначений;
  • 🔗 массиву результатов пакетного запроса.

Механика у всех одна. Ошибку дают пять типовых ситуаций:

  1. Граница цикла на единицу больше. Написали Для Инд = 0 По Массив.Количество() Цикл вместо Количество() - 1. Последняя итерация лезет за край.
  2. Обращение к первому элементу пустой коллекции. Выборка = Результат.Выгрузить()[0] при пустом результате запроса.
  3. Удаление элементов в прямом цикле. После каждого Удалить() коллекция короче, а счётчик идёт дальше.
  4. Перепутаны индекс и идентификатор. Номер строки формы или поле НомерСтроки используют как индекс.
  5. Разбор строки без проверки. Части = СтрРазделить(Строка, ";"), затем Части[1], хотя разделителя в строке не было.

Пятый случай коварен: код годами работает на «правильных» данных и падает на первой нестандартной строке из файла обмена.

Помимо чтения, индекс проверяют и методы изменения коллекции. Массив.Удалить(Инд) и Массив.Установить(Инд, Значение) требуют, чтобы элемент с таким номером уже существовал. Добавить новый элемент в конец можно только через Добавить(): запись Массив[Массив.Количество()] = Значение даёт ту же ошибку.

Ещё одна деталь — конструктор Новый Массив(3). Он создаёт массив из трёх элементов со значением Неопределено, а не пустой массив «на три места». Если затем вызвать Добавить(), новый элемент получит индекс 3, а не 0. Код, который ждал данные в Массив[0], получит Неопределено и упадёт уже на следующем шаге с другим сообщением.

Как найти строку с ошибкой в режиме Предприятия

Сначала выясните, где именно упал код. Без этого любые действия — гадание.

Подробный текст сообщения

В окне ошибки откройте подробное описание (кнопка или ссылка «Подробно»). Там будет строка такого вида:

{Обработка.ЗагрузкаПрайса.Форма.Форма.Форма(214)}: Индекс находится за границами диапазона
	Цена = Колонки[5];

В фигурных скобках — объект и модуль, в круглых — номер строки. Ниже платформа часто показывает саму строку кода. Это уже половина диагностики.

По первому слову в пути понятно, чей код сломался:

Начало путиЧей кодКто исправляет
ОбщийМодуль.…, Документ.…, Справочник.…типовая конфигурация или доработкаразработчик или обновление релиза
ВнешняяОбработка.…, ВнешнийОтчет.…внешний файл .epf/.erfавтор обработки
объект с префиксом расширениярасширение конфигурацииавтор расширения

Журнал регистрации

Если пользователь закрыл окно, текст ошибки остаётся в журнале. В БП 3.0 и ЗУП 3.1 он открывается через Администрирование → Обслуживание → Журнал регистрации. Отберите события по уровню «Ошибка» и периоду, когда случился сбой. В комментарии события будет тот же путь к модулю.

Отладчик с остановкой по ошибке

Когда номер строки известен, а причина нет, нужен отладчик. В Конфигураторе:

  1. Включите Отладка → Остановка по ошибке… и поставьте флажок остановки.
  2. В поле фильтра впишите фрагмент за границами диапазона, чтобы не ловить другие исключения.
  3. Запустите клиент через Отладка → Начать отладку.
  4. Повторите действие пользователя. Отладчик остановится на проблемной строке.
  5. В табло посмотрите Количество() коллекции и значение индекса.

Ошибка в фоновом задании или обмене

Регламентные задания и обмены работают без окна, сообщение пользователь не видит. Признак — «зависшая» загрузка или не выполненное задание. Текст ошибки с путём к модулю ищите в том же журнале регистрации, отобрав события по имени задания или обмена. Чтобы поймать такой сбой в отладчике, проще всего вызвать ту же процедуру вручную из обработки на копии базы: тогда она выполнится в обычном сеансе и остановится на ошибке.

Если ошибка воспроизводится только на рабочей базе, а отладку там включать нельзя, выгрузите копию в файловый вариант и ловите сбой на ней.

Диагностика: выберите симптом

Табличные части: удаление строк и путаница с номерами

Самый частый источник ошибки в прикладном коде — табличные части документов.

Удаление в прямом цикле

Вот классический сбой:

// Ошибка: граница цикла вычислена один раз, а строки удаляются
Для Инд = 0 По Товары.Количество() - 1 Цикл
	Если Товары[Инд].Количество = 0 Тогда
		Товары.Удалить(Инд);
	КонецЕсли;
КонецЦикла;

Граница По вычисляется один раз перед стартом цикла. Было 10 строк, удалили 2 — на Инд = 8 строки уже нет. Правильно идти с конца:

Инд = Товары.Количество() - 1;
Пока Инд >= 0 Цикл
	Если Товары[Инд].Количество = 0 Тогда
		Товары.Удалить(Инд);
	КонецЕсли;
	Инд = Инд - 1;
КонецЦикла;

При обратном проходе удаление строки не сдвигает те, что ещё впереди по ходу цикла.

Для Каждого с удалением внутри тоже ошибочен, хотя исключения не даёт: он молча пропускает строки. Это хуже, потому что ошибку никто не видит.

Если условие простое, цикл вообще не нужен. Найдите строки через НайтиСтроки() и удаляйте их по ссылкам на строки, а не по номерам.

НомерСтроки — не индекс

У строки табличной части есть поле НомерСтроки. Оно начинается с единицы, а индекс — с нуля. Код Товары[Строка.НомерСтроки] всегда смотрит на следующую строку. А на последней строке падает с ошибкой.

Нужен индекс — берите Товары.Индекс(Строка). Нужна сама строка — работайте со ссылкой на неё, без номеров.

Строка таблицы значений по номеру колонки

К строке ТаблицыЗначений можно обратиться по номеру колонки: Строка[3]. Так часто пишут загрузку из Excel или CSV. Если в файле меньше колонок, чем ожидал код, получите ту же ошибку. Перед чтением проверяйте ТЗ.Колонки.Количество(), а лучше обращайтесь к колонкам по именам.

Формы и динамические списки

На управляемых формах ошибка возникает там, где путают три разные вещи: индекс, идентификатор строки и выделение.

ТекущаяСтрока табличной части формы

Для табличной части на форме свойство Элементы.Товары.ТекущаяСтрока возвращает идентификатор строки, а не её индекс. Идентификатор — служебное число, он не равен позиции строки, особенно после удалений и сортировки.

// Ошибка: идентификатор используется как индекс
ДанныеСтроки = Объект.Товары[Элементы.Товары.ТекущаяСтрока];

// Правильно
ИдСтроки = Элементы.Товары.ТекущаяСтрока;
Если ИдСтроки <> Неопределено Тогда
	ДанныеСтроки = Объект.Товары.НайтиПоИдентификатору(ИдСтроки);
КонецЕсли;

Ещё проще взять Элементы.Товары.ТекущиеДанные — это данные активной строки. Но и его проверяйте на Неопределено: в пустой таблице активной строки нет.

Выделенные строки динамического списка

У динамического списка Элементы.Список.ВыделенныеСтроки — массив ключей выделенных строк. Если пользователь ничего не выделил, массив пуст, и ВыделенныеСтроки[0] падает.

Выделенные = Элементы.Список.ВыделенныеСтроки;
Если Выделенные.Количество() = 0 Тогда
	ПоказатьПредупреждение(, "Выделите хотя бы одну строку.");
	Возврат;
КонецЕсли;

Для динамического списка ТекущаяСтрока — это ссылка на объект или ключ записи, а не число. Использовать её как индекс нельзя вовсе.

Клиент, сервер и устаревшие индексы

Ещё одна ловушка — индекс, запомненный до вызова сервера. Клиент сохранил номер строки 5, вызвал серверную процедуру, а та пересобрала табличную часть. Вернулись на клиент — строк уже 3.

Передавайте на сервер идентификаторы строк, а не индексы, и после серверного вызова заново получайте данные.

Запросы, пакеты и временные таблицы

Пустой результат запроса

// Ошибка на пустой выборке
Цена = Запрос.Выполнить().Выгрузить()[0].Цена;

Если записей нет, таблица пуста, элемента с индексом 0 нет. Надёжный вариант — через выборку:

Выборка = Запрос.Выполнить().Выбрать();
Цена = 0;
Если Выборка.Следующий() Тогда
	Цена = Выборка.Цена;
КонецЕсли;

Если нужна только проверка наличия данных, используйте Результат.Пустой() до выгрузки.

Номер результата в пакете

Запрос.ВыполнитьПакет() возвращает массив результатов по каждому запросу пакета. Сюда входят и запросы ПОМЕСТИТЬ, создающие временные таблицы, и УНИЧТОЖИТЬ.

Пример. В пакете было четыре запроса, итог брали как Результаты[3]. Разработчик удалил ненужную временную таблицу — запросов стало три. Индекс 3 теперь за границей.

Два способа обезопасить себя:

  • 🔚 брать последний результат как Результаты[Результаты.ВГраница()];
  • 🧩 вынести нужный запрос последним и не завязываться на порядковые номера промежуточных.

ВГраница() у пустого массива возвращает -1, поэтому цикл Для Инд = 0 По Массив.ВГраница() по пустому массиву не выполнится ни разу. Это удобная и безопасная форма записи.

Выгрузка колонки

Результат.Выгрузить().ВыгрузитьКолонку("Ссылка") даёт массив. Обращение Массив[0] без проверки количества — тот же случай, что и с пустой таблицей.

Что делать пользователю без доступа к коду

Бухгалтер или кладовщик код не правит, но решить проблему может.

Перед обращением к разработчику
Выполнено: 0 / 5

Что помогает, а что нет:

ДействиеКогда помогаетРиск
Обновить внешнюю обработкупуть начинается с ВнешняяОбработканет
Отключить расширениеошибка в объекте расширенияпропадут функции расширения
Обновить конфигурациюошибка в типовом модуле, есть свежий релизнужна резервная копия
Тестирование и исправлениепочти никогда при этой ошибкедолго, нужен монопольный режим

Тестирование и исправление информационной базы чинит физическую структуру данных. Логическую ошибку в алгоритме оно не исправит. Запускать его ради этого сообщения нет смысла.

Как писать код, который не выходит за границы

Несколько правил закрывают почти все сценарии.

  1. Проверяйте количество перед обращением. Любое [0], [1] должно стоять после Если Коллекция.Количество() > N.
  2. Используйте ВГраница() в циклах. Запись Для Инд = 0 По Массив.ВГраница() не ошибается на единицу.
  3. Удаляйте только с конца. Обратный проход — единственный безопасный способ удалить элементы по индексу.
  4. Предпочитайте Для Каждого для чтения. Если номер не нужен, индексы не нужны тоже.
  5. Обращайтесь к колонкам по имени. Номер колонки ломается при первом изменении файла или запроса.
  6. Не храните индексы между вызовами. Для строк формы — идентификаторы, для объектов — ссылки.
  7. Проверяйте результат СтрРазделить. Сколько частей вернётся, зависит от данных, а не от ожиданий.

Пример разбора строки с проверкой:

Части = СтрРазделить(СтрокаФайла, ";", Истина);
Если Части.Количество() < 3 Тогда
	ТекстОшибки = СтрШаблон("Строка %1: ожидалось 3 поля, получено %2",
		Формат(НомерСтроки, "ЧГ=0"), Формат(Части.Количество(), "ЧГ=0"));
	Сообщить(ТекстОшибки);
	Продолжить;
КонецЕсли;
Артикул = Части[0];
Цена    = Части[2];

Третий параметр Истина в СтрРазделить сохраняет пустые части. Это значение по умолчанию, но его полезно писать явно: если кто-то поменяет его на Ложь, строка "А;;10" даст две части вместо трёх, и Части[2] упадёт.

Не прячьте ошибку в Попытка … Исключение без обработки. Код перестанет падать, но начнёт молча терять данные: строка не загрузилась, а никто об этом не узнал.

Частые вопросы

Почему ошибка появилась после обновления, хотя раньше всё работало?
В новом релизе изменилась структура: колонки запроса, табличные части, порядок пакета. Внешние обработки и расширения, написанные под старую структуру, начинают обращаться к несуществующим элементам.
Можно ли отключить проверку границ массива в 1С?
Нет. Платформа всегда проверяет индекс и вызывает исключение. Исправлять нужно само обращение.
Ошибка возникает только у одного пользователя. Это код или данные?
Обычно данные или настройки: у пользователя свой отбор, вариант отчёта или набор выделенных строк. Повторите его действия под его учётной записью в копии базы.
Чем отличается «за границами диапазона» от «Поле объекта не обнаружено»?
Первое — обращение по номеру к отсутствующему элементу. Второе — обращение по имени к отсутствующему свойству или колонке. Лечатся похоже, но ищут разное.
Помогает ли очистка кэша при этой ошибке?
Как правило, нет. Кэш влияет на отображение конфигурации, а выход за границы возникает при выполнении алгоритма на конкретных данных.