oraios/serena · error
Error: No matches of search expression found.
Error message
Error: No matches of search expression found.
What it means
replace() uses re.subn and raises when zero matches were found for the search expression. Since these replacements are meant to be surgical edits, a zero-match result indicates the needle no longer matches the content, and the library fails loudly instead of no-op-ing.
Source
Thrown at src/serena/util/text_utils.py:474
:return: the updated content after performing the replacement
"""
if self.mode == "literal":
regex = re.escape(needle)
elif self.mode == "regex":
regex = needle
else:
raise ValueError(f"Invalid mode: '{self.mode}', expected 'literal' or 'regex'.")
regex_flags = (re.MULTILINE | re.DOTALL) if self.regex_multiline else 0
# create replacement function with validation and backreference handling
repl_fn = self._create_replacement_function(regex, repl, regex_flags=regex_flags)
# perform replacement
updated_content, n = re.subn(regex, repl_fn, content, flags=regex_flags)
if n == 0:
raise ValueError("Error: No matches of search expression found.")
if not self.allow_multiple_occurrences and n > 1:
raise ValueError(
f"Expression matches {n} occurrences. "
"Please revise the expression to be more specific or enable allow_multiple_occurrences if this is expected."
)
return updated_content
@dataclass
class ReplacementOccurrence:
"""A single prospective replacement of a pattern match within one file."""
occurrence_id: str
"""stable, content-anchored identifier: '<relative_path>:<index_in_file>@<digest>'"""
relative_path: str
index_in_file: int
"""0-based index of this match among the matches within its file (in position order)"""
start: intView on GitHub (pinned to 7fcbca7e62)
Solutions
- Print/search the actual content to confirm the exact current text and update the needle
- Escape regex metacharacters or switch to mode='literal'
- Relax the regex (e.g. use .*? for variable whitespace) if formatting drifts
- Check whether an earlier replacement in a chain already modified the target text
Example fix
// before
replacer.replace(content, 'def old_name(.*?', '...') # function renamed
// after
if re.search(r'def old_name\(', content):
...
else:
content = replacer.replace(content, r'def \w+\(', '...') # flexible pattern Defensive patterns
Strategy: validation
Validate before calling
if re.search(regex, content, flags=re.MULTILINE|re.DOTALL) is None:
raise LookupError('needle not present; skipping replacement') Try / catch
try:
updated = editor.replace(content, needle, repl)
except ValueError as e:
if 'No matches' in str(e):
logging.warning('needle not found; content may have changed')
updated = content
else:
raise Prevention
- Search the live content for the exact text before replacing
- Use literal mode or escape metacharacters
- Allow flexible whitespace with \\s* / .*? to survive reformatting
- Pin/pattern against the file version you target
When it happens
Trigger: Calling replace() where the needle/regex does not occur in content at all — due to a typo, changed file content, or a regex escaping mistake.
Common situations: Library/framework upgrade changed the file text so templates no longer match; editing files whose formatting (whitespace, quotes) differs from the assumed pattern; regex specials like '(' not escaped in literal-mode confusion.
Related errors
- No match found for regex: {regex}
- Match is ambiguous: the search pattern matches multiple over
- Expression matches {n} occurrences. Please revise the expres
- Match must be unique; found {len(matches)} matches for regex
- Regex must contain exactly one group to capture the position
AI-assisted analysis of oraios/serena@7fcbca7e62 (2026-08-29).
Data as JSON: /api/errors/b0d68b15d0c18409.
Report an issue: GitHub.