#8193·autogen

TextCanvas.apply_patch accepts stale hunks without validating context lines

Author: bossjoker1Created Sep 4, 2026Updated Sep 14, 2026

Describe the bug

TextCanvas.apply_patch() says it applies hunks with context-line validation. A unified diff prepared from an older revision is nevertheless applied to the latest revision when its removed/context line no longer matches.

The call returns PATCH APPLIED, creates a new revision, and makes the stale edit the latest content.

Reproduction

import asyncio
import difflib

from autogen_core import CancellationToken
from autogen_ext.memory.canvas import TextCanvasMemory

BASE = "service=payments\nregion=Singapore\nstatus=active\n"
EDIT_A = "service=payments\nregion=Tokyo\nstatus=active\n"
EDIT_B = "service=payments\nregion=London\nstatus=active\n"


def make_diff(old: str, new: str) -> str:
    return "".join(
        difflib.unified_diff(
            old.splitlines(keepends=True),
            new.splitlines(keepends=True),
            fromfile="state.txt",
            tofile="state.txt",
        )
    )


async def main() -> None:
    memory = TextCanvasMemory()
    update = memory.get_update_file_tool()
    apply_patch = memory.get_apply_patch_tool()
    token = CancellationToken()

    await update.run_json(
        {"filename": "state.txt", "new_content": BASE}, token
    )

    # Writer A prepares a patch against revision 1.
    stale_patch = make_diff(BASE, EDIT_A)

    # Writer B changes the same line, producing revision 2.
    await update.run_json(
        {"filename": "state.txt", "new_content": EDIT_B}, token
    )

    result = await apply_patch.run_json(
        {"filename": "state.txt", "patch_text": stale_patch}, token
    )

    print(result.status)
    print(memory.canvas.list_files())
    print(memory.canvas.get_latest_content("state.txt"), end="")


asyncio.run(main())

Run with:

pip install "autogen-ext[canvas]==0.7.5"
python repro.py

Observed output:

PATCH APPLIED
{'state.txt': 3}
service=payments
region=Tokyo
status=active

Expected behavior

The patch should be rejected because its source hunk removes region=Singapore, while the latest revision contains region=London. Revision 2 should remain latest.

As a control, a patch generated from the actual revision-2 content applies successfully.

Environment and validation

  • autogen-core==0.7.5
  • autogen-ext==0.7.5
  • unidiff==0.7.5
  • Python 3.13.13
  • Ubuntu
  • Also reproduced against current main commit 027ecf0a379bcc1d09956d46d12d44a3ad9cee14
  • Repeated 5/5 times on the release and 5/5 times on main
  • No model provider or embedding service is involved

I also independently reproduced the release case in a fresh environment.

Implementation observation

PatchSet parses the diff, but the current loop constructs a replacement block and assigns it to working_lines[start:end] without comparing the hunk's source/context lines to that slice.

TextCanvas is experimental, and the previous revision remains in revision history. The reported behavior is therefore limited to a stale edit silently becoming the current/latest revision; this report does not claim unrecoverable data loss.