Skip to content

BUG: UnifiedDiffCoder.apply_edits omits the partial-application notice #5573

Description

@yifanxiong272

Issue

Summary

In aider/coders/udiff_coder.py, UnifiedDiffCoder.apply_edits omits its partial-application notice when one hunk succeeds and another hunk fails.

The successful edit is written before ValueError is raised, but the error does not include the existing other_hunks_applied message indicating that some hunks were applied.

Steps to reproduce

  1. Check out Aider main at commit 5dc9490bb35f9729ef2c95d00a19ccd30c26339c.

  2. Add the following test inside TestUnifiedDiffCoder in tests/basic/test_udiff.py:

def test_apply_edits_reports_partial_success(self):
    from pathlib import Path

    from aider.coders import Coder
    from aider.coders.udiff_coder import other_hunks_applied
    from aider.io import InputOutput
    from aider.models import Model
    from aider.utils import GitTemporaryDirectory

    with GitTemporaryDirectory():
        success = Path("success.txt")
        failure = Path("failure.txt")
        success.write_text("old\n")
        failure.write_text("different\n")

        coder = Coder.create(
            Model("gpt-3.5-turbo"),
            "udiff",
            io=InputOutput(yes=True),
            fnames=[str(success), str(failure)],
            stream=False,
        )

        edits = [
            (str(success), ["-old\n", "+new\n"]),
            (str(failure), ["-missing\n", "+replacement\n"]),
        ]

        with self.assertRaises(ValueError) as error:
            coder.apply_edits(edits)

        self.assertEqual(success.read_text(), "new\n")
        self.assertIn(
            other_hunks_applied,
            str(error.exception),
        )
  1. Run:
python -m pytest tests/basic/test_udiff.py -q
  1. Observe that the newly added assertion fails while the four existing tests pass.

Expected behavior

For the focused batch, one of the two unique hunks is written successfully and the other produces an error. The raised ValueError is expected to include the module's existing other_hunks_applied marker:

Note: some hunks did apply successfully. See the updated source code shown above.

This marker is defined in aider/coders/udiff_coder.py. The same method contains an errors += other_hunks_applied statement guarded by len(errors) < len(uniq).

Actual behavior

success.txt is changed from old to new, confirming that one hunk was applied.

UnifiedDiffCoder.apply_edits then raises ValueError for failure.txt, but the exception does not include other_hunks_applied. The raised message describes only the failed hunk even though the successful edit has already been written.

The focused assertion that fails is:

self.assertIn(
    other_hunks_applied,
    str(error.exception),
)

The focused run summary is:

F....                                                                    [100%]

FAILED tests/basic/test_udiff.py::TestUnifiedDiffCoder::test_apply_edits_reports_partial_success
1 failed, 4 passed in 2.23s

Impact

A mixed-success edit leaves the workspace partially modified while reporting only the failed hunk.

The raised error does not include the function's existing partial-application notice, so the partial-success state is not disclosed in the error message.

Root cause

UnifiedDiffCoder.apply_edits initially stores one entry in errors for each failed hunk. It then joins the entries into a string before comparing its length with the number of submitted hunks:

if errors:
    errors = "\n\n".join(errors)
    if len(errors) < len(uniq):
        errors += other_hunks_applied
    raise ValueError(errors)

After the join, errors is a string, so len(errors) is the number of characters in the error message rather than the number of failed hunks.

For one failed hunk out of two unique hunks, the error string contains more than two characters. The condition is therefore false and other_hunks_applied is not appended.

Possible fix direction

Preserve the failed-hunk count before joining the error messages:

failed_count = len(errors)
message = "\n\n".join(errors)

if failed_count < len(uniq):
    message += other_hunks_applied

raise ValueError(message)

Regression coverage should include all-success, all-failure, and mixed-success batches while verifying both the resulting file contents and error message.

Version and model info

Aider: 0.86.3.dev, main@5dc9490bb35f9729ef2c95d00a19ccd30c26339c
Python: 3.12.13
Operating system: macOS 15.7.3
Installation: source checkout; focused pytest reproduction
Edit format: udiff
Model: gpt-3.5-turbo model metadata used only to construct the coder; no provider request is made
Configuration: InputOutput(yes=True), stream=False

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