How to Fix: ValueError: substring not found

3D illustration of a detective robot failing to find a specific pattern on a wall of text, representing the substring not found error.

This is a common ValueError that happens when you are searching for a piece of text (a “substring”) inside a string, and it doesn’t exist. The message ValueError substring not found can be confusing the first time you see it.

This Error only happens when you use the .index() method.

โšก Quick Fix: ValueError: substring not found โ€” Python .find() and in Keyword Fix for Safe String Search Instead of .index()

You called .index() on a string that doesn’t contain the substring โ€” .index() is strict and crashes immediately when it can’t find an exact match.

# WRONG โ€” "goodbye" isn't in the string, .index() crashes
message = "Hello, world!"
position = message.index("goodbye")   # ValueError: substring not found

# WRONG โ€” case-sensitive mismatch: "World" is not "world"
position = message.index("World")     # ValueError: substring not found

# FIX 1 โ€” .find(): returns -1 when the substring is missing, never crashes
position = message.find("goodbye")
if position == -1:
    print("Not found.")
else:
    print(f"Found at index {position}.")

# FIX 2 โ€” in keyword: the Pythonic check before committing to .index()
if "goodbye" in message:
    position = message.index("goodbye")
    print(f"Found at index {position}.")
else:
    print("Not found.")

# FIX 3 โ€” try/except: use when missing substring is rare and ignorable
try:
    position = message.index("goodbye")
except ValueError:
    position = -1

Use .find() as your default for any search where the substring might not exist.

The Cause: .index() vs. .find()

Python gives you two ways to find the position of text in a string:

  1. .index(text): This method is strict. It believes the text must exist. If it finds it, it returns the starting position (e.g., 0). If it doesn’t find it, it crashes.
  2. .find(text): This method is “safe.” If it finds it, it returns the starting position. If it doesn’t find it, it returns -1 (and doesn’t crash).

Problem Code:

message = "Hello, world!"
# Try to find a substring that isn't there
position = message.index("goodbye")
# CRASH! ValueError: substring not found

The Fix: Use .find() or an in Check

Choose the fix that matches your goal.

Fix 1: Use .find() (The “Safe” Way)

This is the best fix if you just want to check if something exists.

message = "Hello, world!"
position = message.find("goodbye")

if position == -1:
    print("Substring was not found.")
else:
    print(f"Substring found at index {position}.")
# Output: Substring was not found. (No crash)

Fix 2: Use the in Keyword (The “Pythonic” Way)

If you just want to know if the substring exists (and you don’t care where it is), use the in keyword.

message = "Hello, world!"

if "goodbye" in message:
    # This code will only run if it's found
    position = message.index("goodbye")
    print(f"Substring found at index {position}.")
else:
    print("Substring was not found.")

ValueError: substring not found โ€” .index() vs .find() and the Three Search Patterns Every Python Developer Needs

ValueError: substring not found fires exclusively from .index(). Python’s string search methods split into two camps: strict methods that crash on a miss, and safe methods that return a sentinel value. Knowing which to reach for eliminates this error permanently.

Two methods search for a substring position. One is strict, one is safe.

.index(sub) crashes with ValueError when sub is missing. Use it only when the substring must exist โ€” parsing a known file format, extracting a required field from a validated string, or any case where a missing substring is a genuine program error that should stop execution immediately.

.find(sub) returns -1 when sub is missing. Use it for all other cases โ€” user input, API responses, CSV data, log files, and any string where the substring might or might not be present. Check the return value against -1 before using the position.

Three patterns cover every real-world substring search.

Position needed, substring guaranteed: .index(). Wrap in try/except ValueError if you want a clean error message instead of a raw traceback.

Position needed, substring uncertain: .find() with an if position != -1: guard.

Existence only, position irrelevant: in keyword. if “error” in log_line: is faster to read, faster to type, and communicates intent immediately โ€” you care whether it’s there, not where.

The case-sensitivity rule applies to all three. “World” and “world” are different substrings. .find(“World”) on “Hello, world!” returns -1. Add .lower() to both sides when case doesn’t matter:

if “goodbye” in message.lower():
position = message.lower().find(“goodbye”)

The re module handles substring search with patterns, wildcards, and case-insensitive flags when exact string matching isn’t enough โ€” import re; re.search(r”go+dbye”, message, re.IGNORECASE) finds “goodbye”, “gooodbye”, and “Goodbye” in one call.

Similar Posts

Leave a Reply