Почему 1С пишет «Индекс находится за границами диапазона» и что с этим делать

· 9 мин

Содержание

Ошибка значит одно: код обратился к элементу массива, таблицы значений или табличной части по номеру, которого в коллекции нет. В 1С 8.3 платформа пишет Индекс находится за границами диапазона, в поиске её чаще называют «индекс за границами массива». Исправить её можно только в коде: найти строку, где берётся элемент по индексу, и проверить, сколько элементов в коллекции на самом деле.

Индексы в 1С начинаются с нуля. В массиве из 5 элементов допустимы номера от 0 до 4. Обращение Массив[5], Массив[-1] или к любому элементу пустого массива сразу останавливает выполнение.

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

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

Почти всегда причина — в одном из шести шаблонов. Каждый легко узнать по коду.

  • 🔢 Граница цикла на единицу больше. Для Сч = 0 По Массив.Количество() Цикл — последний проход обращается к несуществующему элементу. Правильная граница — Количество() - 1.
  • 📭 Пустая коллекция. Код берёт Массив[0] или последний элемент Массив[Массив.Количество() - 1], а массив пуст. Например, запрос ничего не вернул.
  • 🗑️ Удаление внутри прямого цикла. После Удалить() элементы сдвигаются, а цикл идёт до старой границы.
  • 🔁 Путаница НомерСтроки и индекса. У строки табличной части НомерСтроки начинается с 1, индекс — с 0. Товары[Строка.НомерСтроки] попадает в следующую строку, а на последней падает.
  • ✂️ Разбор строки без проверки. Части = СтрРазделить(Текст, ";"), затем Части[2] — а в тексте оказалось только два фрагмента.
  • 🔍 Результат поиска взят как индекс без проверки. Найти() вернул Неопределено, а код подставил его в квадратные скобки.

Отдельный случай — индекс посчитан из внешних данных: номер колонки из файла Excel, позиция из JSON, номер из параметра обработки. Такие значения надо проверять всегда.

Как найти строку с ошибкой отладчиком

Сначала узнайте модуль и номер строки. В окне ошибки нажмите «Подробно» (в тонком клиенте — ссылка с подробной информацией). Там будет текст вида:

{ОбщийМодуль.ОбработкаЗаказов.Модуль(214)}: Ошибка при вызове метода контекста
	Индекс находится за границами диапазона

В фигурных скобках — модуль и номер строки, здесь строка 214. Та же запись попадает в журнал регистрации: Администрирование → Обслуживание → Журнал регистрации, отбор по уровню «Ошибка».

Остановка прямо на ошибке

Если ошибка возникает не всегда, удобнее поймать момент.

  1. Откройте базу в Конфигураторе и запустите отладку: Отладка → Начать отладку (F5).
  2. Включите Отладка → Остановка по ошибке и отметьте флажок остановки по любой ошибке или впишите текст Индекс находится за границами диапазона.
  3. Повторите действие пользователя в запущенном клиенте.
  4. Отладчик остановится на проблемной строке. Выделите выражение с индексом и нажмите Shift+F9 — «Вычислить выражение».
  5. Сравните значение индекса с Коллекция.Количество(). Обычно сразу видно: индекс равен количеству или коллекция пуста.

Условная точка останова

В длинном цикле по 10 000 строк смотреть каждую итерацию бессмысленно. Поставьте точку останова с условием (Ctrl+Shift+F9) и впишите, например, Сч >= Массив.Количество(). Отладчик сработает только на нужном проходе.

Если код выполняется на сервере, проверьте, что отладка серверных вызовов включена. В клиент-серверном варианте сервер 1С должен быть запущен с ключом отладки, иначе точки останова в серверных модулях не сработают.

Для управляемых форм полезно «Табло»: добавьте туда коллекцию и раскройте её. Видно количество элементов и содержимое каждого.

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

Циклы и проверки, которые не выходят за границы

Прямой обход по индексу

Когда индекс нужен, граница считается от количества минус один:

Для Индекс = 0 По Массив.Количество() - 1 Цикл
	Значение = Массив[Индекс];
	// обработка
КонецЦикла;

Для пустого массива граница станет -1, и цикл не выполнится ни разу. Это корректно. Вместо Количество() - 1 можно писать Массив.ВГраница() — метод возвращает индекс последнего элемента, для пустого массива -1.

Если индекс не нужен, используйте Для Каждого Элемент Из Массив Цикл. В таком цикле выйти за границы невозможно.

Удаление элементов: только с конца

Удаление в прямом цикле сдвигает элементы. Индексы, которые цикл ещё не прошёл, теперь указывают на другие данные, а последние — в пустоту. Обходите коллекцию с конца, зафиксировав количество до цикла:

Всего = Массив.Количество();
Для Сч = 1 По Всего Цикл
	Индекс = Всего - Сч;
	Если Массив[Индекс] = Неопределено Тогда
		Массив.Удалить(Индекс);
	КонецЕсли;
КонецЦикла;

При Всего = 5 цикл проходит индексы 4, 3, 2, 1, 0. Удаление элемента с большим индексом не сдвигает элементы с меньшими, поэтому каждый индекс остаётся верным.

Не удаляйте элементы внутри Для Каждого. Ошибки может и не быть, но часть элементов будет пропущена. Такой баг заметить ещё труднее, чем падение.

Проверка перед обращением

Любое обращение «к первому» или «к последнему» элементу защищайте проверкой:

Если Массив.Количество() > 0 Тогда
	Последний = Массив[Массив.Количество() - 1];
Иначе
	Последний = Неопределено;
КонецЕсли;

Для разбора строки проверяйте число фрагментов:

Части = СтрРазделить(Текст, ";");
Если Части.Количество() < 3 Тогда
	ВызватьИсключение СтрШаблон("Ожидалось 3 поля, получено %1: %2",
		Части.Количество(), Текст);
КонецЕсли;
Сумма = Число(Части[2]);

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

Метод Найти и индексы: где теряется проверка

У массива Найти() возвращает индекс найденного элемента или Неопределено. Ошибка появляется, когда результат сразу идёт в работу:

// Плохо: если значения нет, Индекс = Неопределено
Индекс = Массив.Найти(Значение);
Массив.Удалить(Индекс);

Правильно — сначала сравнить с Неопределено:

Индекс = Массив.Найти(Значение);
Если Индекс <> Неопределено Тогда
	Массив.Удалить(Индекс);
КонецЕсли;

Ещё одна ловушка — арифметика вокруг найденного индекса. Код ищет элемент и берёт следующий: Массив[Индекс + 1]. Если найден последний элемент, следующего нет. Проверяйте Индекс < Массив.Количество() - 1.

У таблицы значений и табличной части Найти() возвращает не индекс, а строку или Неопределено. Если программист привык к массиву и пишет ТЗ[ТЗ.Найти(Значение, "Номенклатура")], код ломается. Номер строки в коллекции даёт метод Индекс():

Строка = ТЗ.Найти(Значение, "Номенклатура");
Если Строка <> Неопределено Тогда
	ИндексСтроки = ТЗ.Индекс(Строка);
КонецЕсли;

Обратите внимание на тип возвращаемого значения в синтакс-помощнике: открыть его можно по Ctrl+F1 на имени метода.

Табличные части и таблицы значений

С табличными частями документов выход за границы случается чаще всего. Причин три.

НомерСтроки против индекса

ЧтоС какого числаПример для 3 строк
Индекс (Товары[i], Товары.Индекс(Строка))с 00, 1, 2
Строка.НомерСтрокис 11, 2, 3
Товары.Количество()—3

Переход между ними: Индекс = Строка.НомерСтроки - 1. Ошибка Товары[Строка.НомерСтроки] на последней строке обращается к индексу 3 из трёх строк — это и есть выход за границы.

Удаление строк по условию

Удаление строк табличной части с нулевым количеством — классический пример. Используйте тот же обратный обход, что и для массива, только с методом Удалить() табличной части. Второй вариант — сначала собрать строки в массив через НайтиСтроки(), затем удалить каждую в отдельном цикле:

Отбор = Новый Структура("Количество", 0);
КУдалению = Объект.Товары.НайтиСтроки(Отбор);
Для Каждого Строка Из КУдалению Цикл
	Объект.Товары.Удалить(Строка);
КонецЦикла;

Здесь цикл идёт по отдельному массиву, а не по самой табличной части, поэтому сдвиг строк ничему не мешает.

Данные формы на клиенте и сервере

В управляемой форме табличная часть на клиенте — это ДанныеФормыКоллекция. Если индекс строки запомнили до серверного вызова, а на сервере строки отфильтровали или пересоздали, сохранённый индекс уже не годится. Передавайте на сервер идентификатор строки (ПолучитьИдентификатор()), а не номер, и ищите строку через НайтиПоИдентификатору().

То же с выделенными строками списка: массив Элементы.Товары.ВыделенныеСтроки содержит идентификаторы, а не индексы.

Результат запроса

Частый сценарий в отчётах и обработках:

ТЗ = Запрос.Выполнить().Выгрузить();
Цена = ТЗ[0].Цена; // падает, если запрос пуст

Проверяйте пустоту до выгрузки: Результат = Запрос.Выполнить(); Если Результат.Пустой() Тогда ... КонецЕсли;. Если нужна одна запись, удобнее выборка: Выборка = Результат.Выбрать(); Если Выборка.Следующий() Тогда .... Вариант с выборкой вообще не использует индексы.

Неочевидные нюансы платформы

Несколько вещей, которые сбивают с толку даже опытных разработчиков.

  • 🧮 Новый Массив(5) создаёт 5 элементов, а не пустой массив. Все пять равны Неопределено. Следующий Добавить() поставит значение под индексом 5, а не 0.
  • 📏 Граница цикла Для вычисляется один раз. Если внутри цикла добавлять элементы, новые не будут обработаны. Если удалять — цикл дойдёт до старой границы и упадёт.
  • 🧊 ФиксированныйМассив поддерживает чтение по индексу с теми же правилами, но не даёт менять состав. Ошибка индекса в нём означает ту же проблему с границей.
  • 🧱 Многомерный массив Новый Массив(3, 4) — это массив из трёх массивов по четыре элемента. М[2][3] допустимо, М[3][0] — нет. Количество строк и столбцов проверяйте отдельно.
  • 🛡️ Попытка ... Исключение не лечит ошибку. Обернуть обращение в попытку и молча продолжить — значит потерять данные без следа. Если исключение неизбежно, запишите ОписаниеОшибки() в журнал регистрации и сообщите пользователю, какая запись не обработана.
  • 🧩 Колонки таблицы значений тоже коллекция. ТЗ.Колонки[7] в таблице из 7 колонок вызовет ту же ошибку. При загрузке из файла число колонок проверяйте до обхода.

Если ошибка появилась в типовой конфигурации

Пользователь видит ошибку в документе или отчёте и не может исправить код. Что делать:

  1. Повторите действие ещё раз и запомните, на каком документе или с какими настройками отчёта она возникает.
  2. Нажмите «Подробно» в окне ошибки и скопируйте весь текст с модулем и номером строки.
  3. Проверьте, есть ли в базе расширения: Администрирование → Печатные формы, отчёты и обработки → Управление расширениями конфигурации. Если модуль в тексте ошибки принадлежит расширению, сообщите автору расширения.
  4. Если ошибка в подключаемой внешней обработке или отчёте — та же логика: передайте текст тому, кто её делал.
  5. Если ошибка в типовом модуле — проверьте, установлен ли актуальный релиз. Похожие ошибки в типовых конфигурациях исправляют в следующих релизах.

Разработчик исправляет типовой код без снятия с поддержки — через расширение. Метод с ошибкой переопределяют аннотацией &ИзменениеИКонтроль и добавляют проверку количества. При следующем обновлении платформа покажет, изменился ли исходный метод.

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

Как не допускать таких ошибок

Короткий набор правил для кода, который работает с коллекциями:

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

Хорошо работает и автоматическая проверка: инструменты статического анализа кода 1С умеют находить часть таких мест. Но условия вроде «запрос может вернуть пустой результат» анализатор не знает, их видит только разработчик.

Чем «Индекс за границами диапазона» отличается от «Поле объекта не обнаружено»?
Первая ошибка — обращение по несуществующему номеру. Вторая — обращение к свойству у значения, где его нет, часто у Неопределено после неудачного поиска.
Почему ошибка возникает у одного пользователя, а у другого нет?
Код один, а данные разные. У одного пользователя в документе есть строки или запрос возвращает записи, у другого — пусто. Ищите условие, при котором коллекция оказывается короче.
Можно ли просто увеличить размер массива?
Нет. Размер массива растёт сам при Добавить(). Ошибка говорит о неверном индексе, а не о нехватке места, и лишние пустые элементы только испортят данные.
Где посмотреть, какие ошибки были у пользователей вчера?
В журнале регистрации с отбором по уровню «Ошибка» и периоду. В комментарии записи видны модуль, строка и текст ошибки.