Repository navigation
Description
Issue
Summary
With --edit-format patch, a reply that creates a new file with *** Add File: <path> is always rejected with ADD Error: File already exists: <path>, and an empty file is left behind at that path. The patch system prompt tells the model to use exactly this marker for new files, so the patch format cannot create files at all.
Steps to reproduce
pip install aider-chat==0.86.0(no API key or network needed; litellm'smock_responsesupplies the reply).- Save as
repro.py:
# patch edit format: "*** Add File" cannot create a new file.
# Requires only `pip install aider-chat==0.86.0`; no API key or network
# (litellm's mock_response supplies the model reply).
import os
import tempfile
from pathlib import Path
from aider import __version__
from aider.coders import Coder
from aider.io import InputOutput
from aider.models import Model
os.chdir(tempfile.mkdtemp())
print("aider", __version__)
reply = """*** Begin Patch
*** Add File: hello.py
+def hello():
+ print("hello")
*** End Patch
"""
model = Model("gpt-4o")
model.extra_params = {"mock_response": reply}
coder = Coder.create(
main_model=model,
edit_format="patch",
io=InputOutput(yes=True, pretty=False, fancy_input=False),
use_git=False,
stream=False,
)
coder.max_reflections = 0 # one attempt is enough to show the failure
coder.run(with_message="Create hello.py with a hello() function.")
path = Path("hello.py")
print("---")
print("hello.py exists:", path.exists())
print("hello.py content:", repr(path.read_text()) if path.exists() else None)- Run
python repro.py.
Actual output
aider 0.86.0
*** Begin Patch
*** Add File: hello.py
+def hello():
+ print("hello")
*** End Patch
Tokens: 10 sent, 20 received. Cost: $0.00023 message, $0.00023 session.
hello.py
The LLM did not conform to the edit format.
https://aider.chat/docs/troubleshooting/edit-errors.html
Error applying action 'ActionType.ADD' to hello.py: ADD Error: File already
exists: hello.py
Only 0 reflections allowed, stopping.
---
hello.py exists: True
hello.py content: ''
main at 5dc9490b prints the same output (only the version line differs). With the default max_reflections, every retry fails the same way, because the empty file from the first attempt is still there.
Expected behavior
hello.py is created with the two lines from the patch.
The patch system prompt asks for this format when the model creates a file:
aider/coders/patch_prompts.pyL153: "ForAddactions, use the*** Add File: [path/to/new/file]marker, followed by the lines of the new file, each preceded by a plus sign+."- The worked example in the same prompt creates
hello.pywith*** Add File: hello.py(L95).
The other edit formats create new files through the same allowed_to_edit path and then write the new content into the file it creates.
Root cause
Coder.apply_updatesrunsprepare_to_edit(edits)beforeapply_edits(edits)(base_coder.pyL2296-L2304).prepare_to_editcallsallowed_to_edit(path). For a path that does not exist yet,allowed_to_editconfirms "Create new file?" and then callsutils.touch_file(full_path), which creates an empty file (base_coder.pyL2206-L2224). In a git repo the new file is alsogit added (onmain, when auto-commits are on).PatchCoder.apply_editsthen handles the ADD action by checkingpath_obj.exists()and raisingDiffError("ADD Error: File already exists: ...")(patch_coder.pyL566-L569). The file always exists at this point because step 2 just created it.DiffErroris aValueError, soapply_updatesreports the reply as malformed and reflects the error to the model. The empty file remains.
Possible fix direction
Make the ADD check see the file system as it was before prepare_to_edit, for example by recording the paths that allowed_to_edit touched and letting ADD write to such a path, or by accepting ADD when the target exists and is empty. A regression test that runs a mocked *** Add File reply through Coder.run would cover the full apply_updates sequence.
Version and model info
Aider: 0.86.0 (PyPI) and main@5dc9490bb35f9729ef2c95d00a19ccd30c26339c (0.86.3.dev0)
Edit format: patch (--edit-format patch)
Model: gpt-4o with litellm mock_response (no real model call)
Python: 3.12.13
Operating system: Ubuntu 22.04.5 LTS
litellm: 1.75.0 (0.86.0) / 1.82.3 (main)
Activity