{"record":{"id":"36616184b5f764aa","repo":"Aider-AI/aider","slug":"bad-missing-filename-the-filename-must-be-alone-o","errorCode":null,"errorMessage":"Bad/missing filename. The filename must be alone on the line before the opening fence {fence[0]}","messagePattern":"Bad/missing filename\\. The filename must be alone on the line before the opening fence (.+?)","errorType":"exception","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"aider/coders/editblock_coder.py","lineNumber":500,"sourceCode":"                i += 1  # Skip the closing ```\n\n            yield None, \"\".join(shell_content)\n            continue\n\n        # Check for SEARCH/REPLACE blocks\n        if head_pattern.match(line.strip()):\n            try:\n                # if next line after HEAD exists and is DIVIDER, it's a new file\n                if i + 1 < len(lines) and divider_pattern.match(lines[i + 1].strip()):\n                    filename = find_filename(lines[max(0, i - 3) : i], fence, None)\n                else:\n                    filename = find_filename(lines[max(0, i - 3) : i], fence, valid_fnames)\n\n                if not filename:\n                    if current_filename:\n                        filename = current_filename\n                    else:\n                        raise ValueError(missing_filename_err.format(fence=fence))\n\n                current_filename = filename\n\n                original_text = []\n                i += 1\n                while i < len(lines) and not divider_pattern.match(lines[i].strip()):\n                    original_text.append(lines[i])\n                    i += 1\n\n                if i >= len(lines) or not divider_pattern.match(lines[i].strip()):\n                    raise ValueError(f\"Expected `{DIVIDER_ERR}`\")\n\n                updated_text = []\n                i += 1\n                while i < len(lines) and not (\n                    updated_pattern.match(lines[i].strip())\n                    or divider_pattern.match(lines[i].strip())\n                ):","sourceCodeStart":482,"sourceCodeEnd":518,"githubUrl":"https://github.com/Aider-AI/aider/blob/5dc9490bb35f9729ef2c95d00a19ccd30c26339c/aider/coders/editblock_coder.py#L482-L518","documentation":"Raised by the SEARCH/REPLACE parser in editblock_coder while iterating fenced blocks: the parser hit an opening fence (matched head_pattern), looked back up to 3 lines for a filename (find_filename), found none, and there was no current_filename carried over from a previous block. The filename must sit alone on the line directly before the opening fence (e.g. ```python).","triggerScenarios":"An LLM reply containing a SEARCH/REPLACE block whose preceding lines contain no recognizable path — filename omitted, embedded in prose, placed after the fence, or written with the fence on the same line. Fires on the first block of a reply (no current_filename fallback) or when filename detection fails for a later block after the previous one had no filename either.","commonSituations":"Models that wrap code in fences without naming the file (DeepSeek-style outputs the code comments on); filenames written as 'File: foo.py' or inside backticks with other text so find_filename's heuristics miss them; chat history where earlier blocks already consumed/lost the filename context.","solutions":["Ensure every SEARCH/REPLACE block is preceded by the bare filename on its own line (foo.py) immediately before the fence.","If driving the coder programmatically, pass valid_fnames for files already in the chat so the parser can pick the filename from context.","Send the error text back to the LLM — aider's retry loop asks the model to re-emit properly labeled blocks.","Add the target file to the chat first (/add foo.py) so current_filename/valid_fnames resolution can succeed."],"exampleFix":"# before (LLM reply)\nHere is the fix:\n```\n<<<<<<< SEARCH\nold line\n=======\nnew line\n>>>>>>> REPLACE\n```\n\n# after\nHere is the fix:\nfoo.py\n```\n<<<<<<< SEARCH\nold line\n=======\nnew line\n>>>>>>> REPLACE\n```","handlingStrategy":"validation","validationCode":"import re\nFENCE = re.compile(r\"^```\\w*$\")\n\ndef blocks_have_filenames(reply: str, known_files: set[str]) -> list[str]:\n    missing = []\n    lines = reply.splitlines()\n    for i, line in enumerate(lines):\n        if FENCE.match(line.strip()):\n            before = [l for l in lines[max(0, i - 3):i] if l.strip()]\n            if not any(l.strip() in known_files for l in before):\n                missing.append(line)\n    return missing  # non-empty → expect the parser error; fix the reply first","typeGuard":null,"tryCatchPattern":"try:\n    edits = list(parse_edits_or_raise(reply))\nexcept ValueError as e:\n    if \"filename must be alone on the line\" in str(e):\n        reply = ask_model_to_relabel_blocks(str(e))  # feed back for retry\n        edits = list(parse_edits_or_raise(reply))\n    else:\n        raise","preventionTips":["System-prompt the model to put the bare path on its own line immediately before every fence.","Add target files to the chat (/add) so valid_fnames gives the parser a fallback.","When pre-processing LLM replies, prepend the missing filename line when only one file is in play."],"tags":["aider","search-replace","parsing","llm-output"],"backgroundTag":null,"analyzedSha":"5dc9490bb35f9729ef2c95d00a19ccd30c26339c","analyzedAt":"2026-08-15T05:40:10.498Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}