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