Skip to content

BUG: a failed file write is reported to the model as "I updated the files." and counted as an edit #5879

Open

Description

@yifanxiong272

Issue

Summary

When writing an edit to disk fails with an OSError (for example PermissionError on a read-only file), Aider prints the write error but then treats the file as edited: it adds the path to aider_edited_files and appends "I updated the files." / "Ok." to the chat history. The file on disk is unchanged, so the next turn tells the model that an edit was applied when it was not. No reflection is sent, so the model gets no chance to react to the failure.

Steps to reproduce

  1. pip install aider-chat==0.86.0 (no API key or network needed; the model reply is scripted).
  2. Save as repro.py:
# A failed write is reported to the model as "I updated the files." and counted as an edit.
# Requires only `pip install aider-chat==0.86.0`; no API key or network (litellm.completion is scripted).
import os
import stat
import tempfile
from pathlib import Path

import litellm

import aider
from aider.coders import Coder
from aider.io import InputOutput
from aider.models import Model

print("aider", aider.__version__)


def completion(**kwargs):
    return litellm.ModelResponse(
        model=kwargs["model"],
        choices=[{"index": 0, "finish_reason": "stop",
                  "message": {"role": "assistant", "content": "settings.py\n```\nVALUE = 2\n```\n"}}],
        usage={"prompt_tokens": 1, "completion_tokens": 1, "total_tokens": 2},
    )


litellm.completion = completion
root = Path(tempfile.mkdtemp())
os.chdir(root)
target = root / "settings.py"
target.write_text("VALUE = 1\n")
target.chmod(stat.S_IRUSR)  # read-only: the write will fail with PermissionError

io = InputOutput(pretty=False, fancy_input=False, yes=True)
coder = Coder.create(main_model=Model("gpt-4o"), io=io, fnames=[str(target)], edit_format="whole",
                     use_git=False, stream=False, map_tokens=0, suggest_shell_commands=False)
coder.run(with_message="Set VALUE to 2.")

print("--- after the run")
print("file content:", repr(target.read_text()))
print("aider_edited_files:", sorted(coder.aider_edited_files))
print("last messages in history:", [(m["role"], m["content"]) for m in coder.done_messages[-2:]])
  1. Run OPENAI_API_KEY=sk-none python repro.py as a non-root user (root ignores the read-only bit).

Actual output

aider 0.86.0
settings.py

```diff
@@ -1 +1 @@
-VALUE = 1
+VALUE = 2
```


Tokens: 1 sent, 1 received. Cost: $0.000013 message, $0.000013 session.
Unable to write file /tmp/tmpXXXX/settings.py after 5 attempts: [Errno 13] 
Permission denied: '/tmp/tmpXXXX/settings.py'
[Errno 13] Permission denied: '/tmp/tmpXXXX/settings.py'
--- after the run
file content: 'VALUE = 1\n'
aider_edited_files: ['settings.py']
last messages in history: [('user', 'I updated the files.'), ('assistant', 'Ok.')]

main at 5dc9490b prints the same (only the version line and temp paths differ). The same happens for other OSErrors raised while writing, e.g. when the target path has been replaced by a directory.

Expected behavior

A failed write should not be recorded as a successful edit. Either the error should be reflected to the model (as the generic except Exception branch of apply_updates already does via reflected_message), or at least the file should not be added to the edited set and "I updated the files." should not be added to the history.

Root cause

apply_updates computes edited from the parsed edits before writing, and its except ANY_GIT_ERROR branch returns that set after only printing the error. ANY_GIT_ERROR includes OSError (repo.py L26-L35), so a write failure from io.write_text takes this branch. send_message then treats the returned set as applied edits: it updates aider_edited_files, auto-commits (in a repo), and moves files_content_gpt_edits_no_repo ("I updated the files.") into the history.

Possible fix direction

Return an empty set (or let the write failure reach the generic branch so reflected_message is set) when the write itself fails, so only files that were actually written are reported as edited.

Version and model info

Aider: 0.86.0 (PyPI) and main@5dc9490bb35f9729ef2c95d00a19ccd30c26339c (0.86.3.dev0)
Model: gpt-4o with litellm.completion scripted (no real model call)
Edit format: whole; no git repo
Python: 3.12.13
Operating system: Ubuntu 22.04.5 LTS

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions