IndexError: list index out of range — исключение Python, которое возникает, когда программа обращается к элементу списка по индексу, которого в списке нет. У списка длины n допустимы индексы от 0 до n - 1 и отрицательные от -n до -1; все остальное вызывает эту ошибку. Ниже — как по traceback найти виноватое обращение, семь типовых причин с исправлением и выбор между проверкой if и try/except.
Содержание
Все примеры проверены на Python 3.14.7 (24.09.2026). Подсвеченные «уголки» ~~~^^^ под строкой в traceback появились в Python 3.11; в более старых версиях будет только строка кода без подсветки.
Минимальный пример и как читать сообщение
scores = [85, 92, 78]
print(scores[2])
print(scores[3])
Первая строка печати выведет 78, вторая упадет:
78
Traceback (most recent call last):
File "/w/a1.py", line 3, in <module>
print(scores[3])
~~~~~~^^^
IndexError: list index out of range
Читать снизу вверх. Последняя строка — тип исключения и текст. Выше — строка кода и номер строки файла (line 3). Символы ^^^ указывают на конкретное обращение по индексу: в длинном выражении вроде data["users"][0]["phones"][0] они покажут, какой именно [...] не нашел элемента.
В списке три элемента, поэтому последний индекс — 2, а не 3. Индексация с нуля — главный источник этой ошибки.
Похожие сообщения: что именно сломалось
Текст после IndexError: подсказывает тип объекта и операцию. Не все похожие ситуации дают IndexError:
| Выражение | Результат в Python 3.14 | Что значит |
|---|---|---|
[1, 2, 3][-4] |
IndexError: list index out of range |
чтение по несуществующему индексу, в том числе отрицательному |
items[0] = "x" при items = [] |
IndexError: list assignment index out of range |
запись в несуществующую позицию: список не растет от присваивания |
[].pop() |
IndexError: pop from empty list |
удаление из пустого списка |
(1, 2)[5] |
IndexError: tuple index out of range |
то же для кортежа |
"abc"[10] |
IndexError: string index out of range |
то же для строки |
{"a": 1}["b"] |
KeyError: 'b' |
у словаря ключи, а не индексы — другое исключение |
[1, 2, 3][10:20] |
[] |
срез за границами не падает, а возвращает пустой список |
Последняя строка таблицы важна: срез молча «проглатывает» выход за границы. Иногда это удобно, но может скрыть ошибку в логике.
Как найти причину за три шага
- По traceback найти строку и конкретное обращение
[...](подсказывают^^^). - Прямо перед этой строкой вывести индекс и длину:
print(f"i={i}, len={len(scores)}"). Допустимый диапазон —0..len-1. - Понять, откуда взялся индекс: из счетчика цикла, из другого списка, из пользовательского ввода, из разбора строки. Ниже — типовые источники.
Семь типовых причин и исправление
1. Условие цикла <= вместо <
scores = [85, 92, 78]
i = 0
while i <= len(scores):
print(scores[i])
i += 1
Результат: напечатаются 85, 92, 78, затем IndexError: list index out of range на scores[i], когда i станет равно 3. Та же ошибка возникает с range(1, len(scores) + 1).
Исправление — строгое неравенство while i < len(scores):, а лучше перебор без индекса: for s in scores:. Если индекс все-таки нужен, используйте enumerate:
scores = [85, 92, 78]
for i, s in enumerate(scores):
print(i, s)
Вывод: 0 85, 1 92, 2 78 — по паре на строке.
2. Два списка разной длины и общий индекс
names = ["Анна", "Борис", "Вера"]
ages = [21, 34]
for i in range(len(names)):
print(names[i], ages[i])
Выведутся две пары, затем ошибка, и ^^^ укажет именно на ages[i]. Индекс взят по длине одного списка, а применен к другому.
Исправление — zip с strict=True (доступен с Python 3.10). Он не прячет расхождение, а сообщает о нем явно:
names = ["Анна", "Борис", "Вера"]
ages = [21, 34]
for name, age in zip(names, ages, strict=True):
print(name, age)
Результат: две пары, затем ValueError: zip() argument 2 is shorter than argument 1. Это честнее, чем обычный zip без strict: тот просто остановится на коротком списке, и «Вера» тихо потеряется. Какой вариант нужен, зависит от задачи: если данные обязаны совпадать по длине — strict=True, если короткий список допустим — обычный zip.
3. Удаление элементов в цикле по индексам
nums = [1, 2, 2, 3, 2]
for i in range(len(nums)):
if nums[i] == 2:
del nums[i]
Результат: IndexError: list index out of range на nums[i]. range(len(nums)) посчитан один раз для длины 5, а список после каждого del укорачивается. Попутно цикл перепрыгивает через соседние элементы.
Исправление — собрать новый список вместо удаления на ходу:
nums = [1, 2, 2, 3, 2]
nums = [n for n in nums if n != 2]
print(nums)
Вывод: [1, 3]. Если нужно менять именно исходный объект, идите по индексам с конца: for i in range(len(nums) - 1, -1, -1): — удаление справа не сдвигает еще не просмотренные элементы, результат тот же [1, 3].
4. Разбор строки: полей меньше, чем ожидалось
line = "Иванов"
surname, name = line.split()[0], line.split()[1]
Результат: IndexError: list index out of range на line.split()[1]: в строке одно слово, и split() вернул список из одного элемента. Типичный случай при чтении файлов, CSV и input(), где встречаются пустые или неполные строки.
Исправление — проверить число полей до обращения:
for line in ["Иванов Иван", "Иванов", ""]:
parts = line.split()
if len(parts) < 2:
print(f"Пропускаю строку {line!r}: ожидалось 2 поля, пришло {len(parts)}")
continue
surname, name = parts[0], parts[1]
print(surname, name)
Вывод:
Иванов Иван
Пропускаю строку 'Иванов': ожидалось 2 поля, пришло 1
Пропускаю строку '': ожидалось 2 поля, пришло 0
5. Первый элемент пустого списка
Код вида results[0] или data["users"][0]["phones"][0] падает, когда поиск ничего не нашел или API вернул пустой массив. Проверка пустоты перед обращением:
data = {"users": [{"name": "Анна", "phones": []}]}
phones = data["users"][0]["phones"]
phone = phones[0] if phones else "не указан"
print(phone)
Вывод: не указан. Пустой список в условии ложен, поэтому if phones — короткая и понятная проверка.
6. Присваивание по индексу вместо добавления
items = []
items[0] = "первый"
Результат: IndexError: list assignment index out of range. В отличие от словаря, список не создает новую позицию при присваивании. Добавлять нужно через append (или insert), а если нужен список фиксированной длины — создать его заранее:
items = []
items.append("первый")
slots = [None] * 3
slots[2] = "третий"
print(items, slots)
Вывод: ['первый'] [None, None, 'третий'].
7. Перепутаны строки и столбцы в двумерном списке
grid = [
[1, 2, 3],
[4, 5, 6],
]
for i in range(3):
for j in range(2):
print(grid[i][j], end=" ")
Результат: напечатается 1 2 4 5, затем ошибка на grid[i] при i = 2: строк две, а не три. Размеры заданы числами «на глаз» и перепутаны местами.
Исправление — брать размеры из самих данных: for i in range(len(grid)): и for j in range(len(grid[i])): (так работает и с рядами разной длины). Проще всего — перебор без индексов: for row in grid: print(*row).
Проверка if или try/except
Когда индекс приходит извне (номер от пользователя, позиция из конфига) и может быть неверным, есть два подхода. Оба — рабочие, выбор зависит от ситуации.
def get_or_default(seq, index, default=None):
"""Элемент по индексу или default, если индекса нет (отрицательные тоже)."""
if -len(seq) <= index < len(seq):
return seq[index]
return default
def get_eafp(seq, index, default=None):
try:
return seq[index]
except IndexError:
return default
scores = [85, 92, 78]
print(get_or_default(scores, 1), get_or_default(scores, 3), get_or_default(scores, -4, 0))
print(get_eafp(scores, 5, "нет"))
Вывод: 92 None 0 и нет. Обратите внимание на условие -len(seq) <= index: проверка только index < len(seq) пропустила бы индекс -4, и функция упала бы.
| Подход | Когда подходит | На что смотреть |
|---|---|---|
Проверка if (LBYL) |
выход за границы — ожидаемый нормальный случай, нужна понятная ветка | учесть отрицательные индексы |
try/except IndexError (EAFP) |
выход за границы редок, код короче | держать в try только одно обращение, иначе поймаете чужой IndexError |
Перебор без индекса, enumerate, zip |
обход коллекции целиком | ошибка индекса невозможна по построению |
Главное правило: try/except и значение по умолчанию уместны, когда отсутствие элемента — допустимая ситуация. Если индекс вне диапазона означает ошибку в логике (как в причинах 1-3 и 7), глушить исключение нельзя — нужно чинить расчет индекса, иначе программа тихо выдаст неверный результат.
Если не получилось
- Ошибка появляется «иногда». Скорее всего, причина в данных: пустой ответ, неполная строка файла, короткий список. Выведите
len(...)и сам объект перед падением на проблемном входе. - В traceback строка без
^^^. У вас Python до 3.11, подсветку отключили (-X no_debug_ranges) или, в Python 3.11-3.12, упавшее выражение занимает всю строку (например,items.pop()отдельной строкой): тогда подсветка не печатается, с 3.13 она есть и в этом случае. Разбейте длинное выражение на несколько переменных, чтобы номер строки указал на одно обращение. - Исправили
<=на<, но падает дальше. Проверьте, не меняется ли список внутри цикла (причина 3) и не взят ли индекс от другого списка (причина 2). - Ошибка внутри библиотеки. Поднимитесь по traceback до последней строки из вашего файла — обычно туда передан пустой или короткий список.
Выводы
IndexError: list index out of rangeзначит, что индекс вне диапазона-len..len-1; у списка из трех элементов последний индекс2.- Traceback читается снизу вверх, а
^^^(с Python 3.11) показывает, какое именно обращение по индексу упало. - Частые причины:
<=в условии цикла, общий индекс для списков разной длины, удаление в цикле, неполные строки при разборе, пустой список, присваивание вместоappend, перепутанные размеры матрицы. - Надежнее всего убрать ручной индекс:
for x in seq,enumerate,zip(..., strict=True). if-проверка илиtry/except— для внешних индексов; ошибку в логике расчета индекса ими не прячут.
Где применяется / связь с практикой
Освойте тему на практике
Ошибки индексации — часть базовой работы с коллекциями: разбор файлов и CSV, обработка ответов API, циклы по данным. Навык читать traceback и строить обход без лишних индексов экономит часы отладки. Системно разобрать списки, циклы, исключения и отладку на практических задачах можно на курсе «Python-разработчик. Базовый уровень». Отдельные темы по Python удобно посмотреть на бесплатных открытых уроках Otus.
FAQ
Почему numpy-массив или pandas дают другое сообщение?
Это другие типы с собственной индексацией: у массива NumPy текст ошибки другой (с указанием оси и размера), а в pandas обращение по метке может дать KeyError. Разбор выше относится к встроенному list.
Можно ли отключить эту ошибку, чтобы список сам возвращал None?
Встроенный list так не умеет. Можно написать функцию-обертку, как get_or_default выше, или унаследоваться от list, но обычно это маскирует ошибки логики.
Почему lst[-1] на пустом списке тоже падает?
Отрицательный индекс считается от конца, но элемент все равно должен существовать: у пустого списка нет ни первого, ни последнего элемента, поэтому [][-1] дает IndexError.



