Aider-AI/aider · error · ValueError
Expected `=======`
Error message
Expected `=======`
What it means
Raised by the SEARCH/REPLACE parser when, after collecting original_text lines following the opening fence/head, it reaches end-of-input or a line that is not the ======= divider. The SEARCH section must be terminated by a line that is exactly ======= before the REPLACE content begins.
Source
Thrown at aider/coders/editblock_coder.py:511
else:
filename = find_filename(lines[max(0, i - 3) : i], fence, valid_fnames)
if not filename:
if current_filename:
filename = current_filename
else:
raise ValueError(missing_filename_err.format(fence=fence))
current_filename = filename
original_text = []
i += 1
while i < len(lines) and not divider_pattern.match(lines[i].strip()):
original_text.append(lines[i])
i += 1
if i >= len(lines) or not divider_pattern.match(lines[i].strip()):
raise ValueError(f"Expected `{DIVIDER_ERR}`")
updated_text = []
i += 1
while i < len(lines) and not (
updated_pattern.match(lines[i].strip())
or divider_pattern.match(lines[i].strip())
):
updated_text.append(lines[i])
i += 1
if i >= len(lines) or not (
updated_pattern.match(lines[i].strip())
or divider_pattern.match(lines[i].strip())
):
raise ValueError(f"Expected `{UPDATED_ERR}` or `{DIVIDER_ERR}`")
yield filename, "".join(original_text), "".join(updated_text)
View on GitHub (pinned to 5dc9490bb3)
Solutions
- Check the error's ^^^ context: it prints the processed lines up to the failure so you can see exactly where the divider is missing or malformed.
- Make ======= exactly seven equals signs on its own line between SEARCH and REPLACE sections.
- If replies are truncated mid-block, raise max_tokens / output token limits for the model.
- Feed the error back to the LLM to re-emit the block with correct markers, or switch to a format the model handles more reliably (udiff/patch).
Example fix
# before foo.py ``` <<<<<<< SEARCH old line ===== (wrong divider) new line >>>>>>> REPLACE ``` # after foo.py ``` <<<<<<< SEARCH old line ======= new line >>>>>>> REPLACE ```
Defensive patterns
Strategy: validation
Validate before calling
def check_block_grammar(text: str) -> str | None:
import re
if not re.search(r"(?m)^=======$", text):
return "missing '=======' divider between SEARCH and REPLACE"
return None # None → safe to parse Try / catch
try:
edits = list(parse_edits_or_raise(reply))
except ValueError as e:
if "Expected `=======`" in str(e):
reply = ask_model_to_fix_dividers(str(e))
edits = list(parse_edits_or_raise(reply))
else:
raise Prevention
- Show the model one fully-formed example block (<<<<<<< SEARCH / ======= / >>>>>>> REPLACE) in the system prompt.
- Watch for max_tokens truncation: incomplete replies are a top source of missing dividers.
- Lint model replies for the exact 7-character markers before handing them to the parser.
When it happens
Trigger: A block where the model forgot the ======= divider, used the wrong number of equals signs (===== or =========), put text on the divider line, or truncated the reply before closing the SEARCH section — the while loop consumes lines until divider_pattern matches, and if it never does (i >= len(lines)) this ValueError fires.
Common situations: Models mangling the strict diff-fenced grammar (extra/missing '=' characters); replies truncated by max_tokens so the block ends mid-SEARCH; copy-pasted diffs that use unified-diff markers instead of the ======= divider.
Related errors
- Bad/missing filename. The filename must be alone on the line
- Expected `>>>>>>> REPLACE` or `=======`
- # {len(failed)} SEARCH/REPLACE {blocks} failed to match!\n
- {processed}\n^^^ {err}
- Delete File action missing path.
AI-assisted analysis of Aider-AI/aider@5dc9490bb3 (2026-08-15).
Data as JSON: /api/errors/f48bb2717df39f28.
Report an issue: GitHub.