Ошибка «Индекс находится за границами массива»: где ломается код и как это найти
Содержание
Код обратился к элементу массива, строки таблицы или коллекции по номеру, которого там нет. Платформа остановила выполнение и показала Индекс находится за границами массива. Чаще всего виноваты нумерация с нуля, пустой список или удаление элементов прямо во время обхода.
Пользователь видит это окно в самый неудобный момент: нажал «Провести», открыл отчёт, запустил обработку. Программист получает в сообщении имя модуля и номер строки. Ниже — что означает ошибка, как быстро найти виновника и как исправить типовые случаи.
Как в 1С устроены индексы и где проходит граница
Массив, таблица значений, список значений, строки табличной части — всё это упорядоченные коллекции. Доступ к элементу идёт по индексу в квадратных скобках: Массив[2].
Главное правило: индексы начинаются с нуля. В массиве из пяти элементов допустимы номера от 0 до 4. Обращение к Массив[5] и даёт ошибку.
| Что проверяем | Как получить | Значение для массива из 5 элементов |
|---|---|---|
| Число элементов | Массив.Количество() | 5 |
| Последний допустимый индекс | Массив.ВГраница() | 4 |
| Индекс первого элемента | — | 0 |
| Индекс для пустого массива | ВГраница() | -1 |
У пустого массива ВГраница() возвращает -1, и любое Массив[0] на нём сразу падает.
Не путайте индекс и номер строки. У строки табличной части свойство НомерСтроки начинается с единицы, а индекс в Объект.Товары[Индекс] — с нуля. Первая строка имеет НомерСтроки = 1, но индекс 0.
Типовые причины: от самых частых к редким
Ошибка обычно возникает в одной из следующих ситуаций.
- 🔢 Счёт с единицы. Цикл
Для Индекс = 1 По Массив.Количество()доходит до индекса, равного количеству, — а такого элемента нет. - 📭 Пустая коллекция. Код берёт
Результат[0], не проверив, что запрос или отбор вообще что-то вернул. - ✂️ Удаление в цикле. Элементы удаляются в прямом порядке, коллекция сокращается, а счётчик ещё помнит старый размер.
- 🔪 Разбор строки.
СтрРазделитьвернула один элемент вместо двух, потому что разделителя в исходной строке не оказалось. - 📍 Устаревший индекс. Индекс запомнили, потом в коллекцию что-то добавили или убрали, и сохранённое число уже указывает не туда.
- 🧩 Чужой код. Внешняя печатная форма, обработка или расширение рассчитывают на структуру данных, которой в вашей базе нет.
Цикл с лишним шагом
Классическая ошибка начинающего:
Для Индекс = 1 По Массив.Количество() Цикл
Сообщить(Массив[Индекс]);
КонецЦикла;
На последнем шаге Индекс равен Количество(), то есть выходит за границу. Правильно так:
Для Индекс = 0 По Массив.ВГраница() Цикл
Сообщить(Массив[Индекс]);
КонецЦикла;
Если индекс не нужен, обходите коллекцию через Для Каждого Элемент Из Массив Цикл — выйти за границу там невозможно.
Удаление элементов при обходе
Допустим, из массива надо убрать все пустые значения. Прямой цикл ломается: после Удалить(2) бывший элемент 3 становится элементом 2 и пропускается, а в конце счётчик уходит за новую границу.
// Неправильно
Для Индекс = 0 По Массив.ВГраница() Цикл
Если Не ЗначениеЗаполнено(Массив[Индекс]) Тогда
Массив.Удалить(Индекс);
КонецЕсли;
КонецЦикла;
Правильно идти с конца к началу: удалённый элемент не сдвигает те, что ещё предстоит проверить.
Индекс = Массив.ВГраница();
Пока Индекс >= 0 Цикл
Если Не ЗначениеЗаполнено(Массив[Индекс]) Тогда
Массив.Удалить(Индекс);
КонецЕсли;
Индекс = Индекс - 1;
КонецЦикла;
Индекс стартует с исходной верхней границы и только уменьшается. Удаление сдвигает лишь элементы правее текущего, а их цикл уже проверил. Для таблицы значений и табличной части приём тот же: начните с ТЧ.Количество() - 1 и идите вниз до нуля.
Индекс = ТЧ.Количество() - 1;
Пока Индекс >= 0 Цикл
Если Не ЗначениеЗаполнено(ТЧ[Индекс].Номенклатура) Тогда
ТЧ.Удалить(Индекс);
КонецЕсли;
Индекс = Индекс - 1;
КонецЦикла;
Пустой результат запроса
Другой частый сценарий: код ожидает ровно одну запись.
Выборка = Запрос.Выполнить().Выбрать();
Если Выборка.Следующий() Тогда
Цена = Выборка.Цена;
КонецЕсли;
Если вместо выборки берут выгрузку в таблицу значений и сразу читают Таблица[0].Цена, то при пустом результате получаем нашу ошибку. Проверка Таблица.Количество() > 0 стоит одной строки.
Как найти место ошибки по тексту сообщения
Раньше чем править, нужно понять, какая именно строка упала. Разберём по шагам.
- Прочитайте сообщение целиком. Нажмите ссылку «Подробно» в окне ошибки: там указаны имя модуля и номер строки в фигурных скобках. Те же сведения видны в отладчике и в журнале регистрации.
- Откройте модуль на этой строке. В Конфигураторе: поиск по конфигурации или переход из списка модулей объекта. Часто видно сразу, какой массив читается.
- Поставьте точку останова на строку выше и повторите действие в режиме отладки.
- Посмотрите значения. Вычислите
Массив.Количество()и значение индекса — расхождение видно за секунду. - Найдите, откуда взялась пустота или лишняя единица. Это уже вопрос логики, а не синтаксиса.
Отлаживайте на копии базы. Повторять проведение документов или закрытие месяца на рабочей базе ради поиска строки опасно: ошибка может сработать в середине операции.
Если строки в сообщении нет
В обычном приложении пользователю часто показывают короткий текст без модуля. Тогда помогут три источника:
- 🧾 Журнал регистрации. Откройте его и отфильтруйте по пользователю и времени, события с уровнем «Ошибка». В записи часто есть место возникновения.
- 🖥️ Режим запуска с отладкой. Тот же сценарий воспроизводят у себя, подключив отладчик к клиенту.
- 📸 Что делал пользователь. Какой документ, какая кнопка, какие данные в табличной части. Без этого воспроизвести ошибку трудно.
Заодно проверьте, подключены ли расширения и внешние обработки. Если ошибка исчезает при их отключении, виновник найден: проблемный код внутри них.
Что делать пользователю без доступа к коду
Вы бухгалтер или кассир, программиста рядом нет. Исправить код вы не можете, но можете сузить поиск и передать разработчику нужное.
Вот что стоит выяснить:
- Воспроизводится ли ошибка каждый раз или только с определённым документом.
- Падает ли то же действие в другом документе, у другого контрагента, за другой период.
- Есть ли ошибка у соседнего пользователя на том же сервере.
- Менялось ли что-то накануне: обновление релиза, подключение расширения, загрузка новой внешней печатной формы.
Если ошибка только на одном документе — дело в его данных. Например, пустая табличная часть, в которой код ожидает хотя бы одну строку. Проверьте заполнение табличных частей и реквизитов документа: добавьте недостающие строки и попробуйте провести снова.
Если ошибка у всех и появилась после обновления — это дефект кода. Нужно сообщить разработчику (или в поддержку конфигурации), приложив точный текст сообщения, названия документа и действия, а также скриншот. Обычно такой случай чинится исправленным релизом.
Если ошибка в печатной форме, попробуйте распечатать другую форму того же документа. Сработала — проблема во внешней форме, подключённой в базу. Отключите её или замените штатной.
Сценарии из практики и их решения
Таблица ниже показывает, как по контексту быстро выйти на причину.
| Где выскочила ошибка | Вероятная причина | Что сделать |
|---|---|---|
| При проведении документа | Код читает первую строку пустой табличной части | Добавить проверку количества строк до обращения |
| При формировании отчёта | Отбор вернул пусто, а код берёт [0] | Обработать случай «данных нет» |
| В печатной форме | Макет рассчитан на другое число колонок или строк | Исправить форму либо вернуть штатную |
| При разборе файла или строки | Нет разделителя, строка короче ожидаемой | Проверить длину массива после СтрРазделить |
| После обновления | Старое расширение переопределяет изменившийся метод | Обновить или отключить расширение |
| При удалении строк в цикле | Прямой обход с удалением | Идти с конца к началу |
Разбор строки на части
Пример из обмена данными: приходит строка Иванов;Москва, а код ждёт три поля: фамилию, город и телефон.
Части = СтрРазделить(Строка, ";");
Если Части.Количество() < 3 Тогда
ВызватьИсключение "Неверный формат строки: ожидалось 3 поля, получено "
+ Части.Количество();
КонецЕсли;
Город = Части[1];
Телефон = Части[2];
Лучше упасть с понятным текстом, чем с общим «индекс за границами». Через полгода никто не вспомнит, какой именно массив имелся в виду.
Табличная часть и выбранная строка
Когда пользователь жмёт кнопку над таблицей, код берёт Элементы.Товары.ТекущаяСтрока. Если строка не выбрана, значение будет Неопределено, и дальнейшие обращения к данным строки приведут к ошибкам. Проверяйте результат до работы с ним.
Идентификатор текущей строки в управляемой форме — это не индекс, а внутренний номер строки. Получить саму строку по нему можно методом НайтиПоИдентификатору, а не обращением в квадратных скобках. Многие ошибки появляются именно из-за подмены одного другим.
Профилактика: привычки, которые убирают ошибку навсегда
Несколько правил, которые разработчик применяет почти автоматически.
- ✅ Перед
[0]проверяйтеКоличество(). Одна строка защиты избавляет от целого класса сбоев. - ✅ Используйте
Для Каждого, если индекс не нужен. Границы коллекции платформа соблюдает сама. - ✅ Для обхода с индексом берите
ВГраница(), а неКоличество(). - ✅ Не запоминайте индексы надолго. После добавления или удаления элементов пересчитайте.
- ✅ Для поиска используйте
Найти. Метод массива возвращаетНеопределено, если значения нет, — проверьте это до использования результата. - ✅ Проверяйте входные данные. Файлы, строки обмена и ответы внешних сервисов не всегда соответствуют формату.
Договоритесь в команде, что делать, когда данных нет. «Вернуть пустое значение», «показать сообщение» или «бросить исключение» — выбор зависит от задачи, но он должен быть осознанным.
При ревью чужого кода ищите три подозрительных шаблона: [0] без проверки, Количество() как верхняя граница цикла и Удалить внутри прямого обхода.
Два мифа, которые мешают найти причину
«Это сбой платформы, надо переустанавливать 1С». Нет. Платформа сработала правильно: ей передали несуществующий номер. Переустановка ничего не изменит, а при ошибке в коде она бессмысленна.
«Помогает перезапуск и чистка кэша». Кэш к границам массива отношения не имеет. Если ошибка исчезла после перезапуска, значит изменились данные в момент запуска или вы просто воспроизвели действие иначе. Причина осталась в коде.
Исключение — когда ошибка плавающая и зависит от данных, например, от наличия записей за конкретный период. Тогда «лечится» не кэш, а пустой период, и ошибка вернётся.
Отладка для сложных случаев
Если простой взгляд на строку причину не показал, подключайте инструменты.
Остановка в момент ошибки
Запустите клиент под отладчиком и включите остановку по ошибке в меню отладки Конфигуратора. Тогда выполнение прервётся не после сообщения, а прямо перед сбоем, и вы увидите состояние всех переменных. Посмотрите стек вызовов: ошибочное обращение часто находится не там, где значение испортили.
Вычисление выражений
В остановленном отладчике вычислите выражение для проверки гипотезы: количество элементов, содержимое массива, значение индекса. Если массив пришёл из функции, вычислите результат её вызова отдельно — так видно, пустая ли она.
Условные точки останова
Если ошибка срабатывает в цикле на тысячной итерации, ставить обычную точку останова бессмысленно. Задайте условие, например Индекс > Массив.ВГраница(), и отладчик остановится ровно на проблемном шаге.
Диагностика на рабочем сервере
Когда ошибку не воспроизвести у себя, но она стабильно есть у пользователей, помогает запись контекста. Добавьте в проблемное место временное сообщение с размером коллекции и значением индекса, запишите их в журнал регистрации, потом уберите. Так вы получаете реальные значения без остановки работы.
Не оставляйте отладочный вывод в рабочем коде. Он засоряет журнал, а в коде, который выполняется для каждой строки документа, ещё и замедляет работу.
Частые вопросы
Почему массив в 1С нумеруется с нуля, а строки табличной части показывают номер с единицы?
НомерСтроки — это отображаемый пользователю номер, он идёт с единицы. Для обращения по индексу нужен первый, а не второй.Чему равен `ВГраница()` у пустого массива?
Для Индекс = 0 По Массив.ВГраница() на пустом массиве просто не выполнится.Одинаково ли ведут себя массив и таблица значений?
Можно ли «проглотить» ошибку через `Попытка … Исключение`?
Попытка оставьте для случаев, когда сбой внешний и предсказать его нельзя.