Python re.findall IndexError: Empty Match Out of Range (2026)

You wrote matches = re.findall(pattern, text) then matches[0] and crashed with IndexError because the pattern didn’t match. re.findall returns an empty list when nothing matches, and indexing into an empty list raises IndexError.

Python re.findall IndexError Empty Match Out of Range (2026)
Python re.findall IndexError Empty Match Out of Range (2026)

📌 Quick answer: If you want “the first match or None,” use re.search(pattern, text) which returns None on no match, instead of re.findall(...)[0]. Check with if m := re.search(...): m.group() using the walrus operator (Python 3.8+).

Pattern 1: Use re.search for “first match or nothing”

re.search returns a Match object on success, None on failure. Much safer than indexing into findall.

import re
m = re.search(r"(\d+)", text)
if m:
    number = m.group(1)
else:
    number = None

Pattern 2: Walrus operator (Python 3.8+)

Cleaner conditional + access in one line.

if m := re.search(r"version: (\d+\.\d+)", text):
    version = m.group(1)
else:
    version = "unknown"

Pattern 3: Default with next()

For “any match from findall” with default.

matches = re.findall(r"\b\w+@\w+\.\w+\b", text)
first_email = next(iter(matches), None)

Pattern 4: re.finditer for streaming matches

For pattern matching in a large string, finditer is lazy and yields Match objects.

for m in re.finditer(r"\d+", text):
    print(m.group(), m.start(), m.end())
# Naturally handles "no matches" case (loop just doesn't execute)

Prevention

  1. Default to re.search() for “find one”, re.findall() for “find all”
  2. Never index into re.findall() result without checking the list is non-empty
  3. Use walrus operator := on Python 3.8+ for cleaner conditional matching
  4. Test with empty/non-matching inputs in unit tests

Frequently Asked Questions

Why does re.findall(…)[0] raise IndexError?

re.findall returns an empty list when nothing matches the pattern. Indexing into an empty list with [0] raises IndexError. Use re.search instead, which returns None on no match.

What’s the difference between re.search, re.findall, and re.finditer?

re.search returns the first Match object (or None). re.findall returns a list of all matches as strings (or tuples if groups). re.finditer returns an iterator of Match objects (lazy, memory-efficient for large texts).

Why does re.findall return tuples sometimes?

When the pattern has multiple capture groups, each match is returned as a tuple of group values. For a single group it’s a list of strings. To always get Match objects, use re.finditer or re.search instead.

How do I check if a regex matched without raising?

result = re.search(pattern, text); if result: … It returns None on no match. Or use re.match for anchored matching (only at string start).

Should I compile my regex with re.compile?

Compile once when the pattern is reused many times (loops, multi-line files). Skip compilation for one-shot uses, re internal caching handles small numbers of recent patterns. Both raise the same errors.

Leave a Comment