Skip to content

edit reports success for a CRLF-only line-ending change that does nothing #45927

Closed

Description

@skyzhao1223

Describe the bug

In the edit tool, the oldString === newString guard runs on the raw input before CRLF normalization. On a CRLF file, oldString and newString that differ only in line-ending style are normalized to the same CRLF text, so the replacement becomes the identity function.

The tool then reports Replacements: 1 (success) while the file is unchanged — the model believes it applied a change (e.g. normalizing line endings) that never happened.

Steps to reproduce

CRLF file containing a\r\nb. Edit with oldString: "a\r\nb", newString: "a\nb".

Expected: either a change or a clear error. Actual: success with Replacements: 1, file unchanged.

Affected code

packages/core/src/tool/edit.ts — the identity check at the top of execute vs. the convertToLineEnding normalization further down.

Suggested fix

Re-check oldString === newString after normalization, or detect the identity replacement and return a no-op error instead of reporting success.

Environment

  • opencode version: latest dev

Activity

  1. github-actions commented on Aug 28, 2026

    @github-actions
    Contributor

    This issue might be related to existing issues. Please check:

  2. devYRPauli commented on Sep 3, 2026

    @devYRPauli

    The same defect is on v2, at packages/core/src/tool/plugin/edit.ts. The raw check sits at line 135 and the line-ending conversion at lines 169-170.

    Measured on v2 at f94eefa, through the registered tool. The file is CRLF and holds before\r\nrest\r\n. The call passes oldString: "before\r\nrest" and newString: "before\nrest".

    status: completed
    content: [{"type":"text","text":"Edited crlf.txt (1 replacement)"}]
    file unchanged: true
    writes: 1
    permission asserts: 1
    

    On v2 the tool also asks for edit approval and writes the file back before it reports the replacement.

    #46849 fixes this on dev. #47088 fixes it on v2. The tool file moved between the two branches, so one diff does not cover the other.

  3. ggbdpq commented on Oct 6, 2026

    @ggbdpq

    I can't reproduce this on current dev — the identity guard inside replace() (packages/opencode/src/tool/edit.ts:683) already rejects this case with No changes to apply: oldString and newString are identical., and the file is left untouched. Verified with a test against a CRLF file (a\r\nb, oldString: "a\r\nb", newString: "a\nb"): it errors instead of reporting success, so the guard fires before any replacement happens. For what it's worth this path is unaffected by #53253, which only changes ending detection for mixed-ending files.

    So on dev this looks fixed or unreachable; the live defect seems to be the v2 copy of the tool that @devYRPauli pointed at (packages/core/src/tool/plugin/edit.ts).

  4. devYRPauli commented on Oct 7, 2026

    @devYRPauli

    packages/opencode/src/tool/edit.ts:683 is the v1 tool. This issue is about the core tool, packages/core/src/tool/edit.ts, and that file still has the defect on dev.

    At ecc4916, the core tool compares the raw strings at line 127 and converts the line endings at lines 163-164. Nothing compares the strings again after the conversion. The v1 tool converts first, at lines 130-131, so its replace() guard sees the converted strings. That is why your test errors.

    The core file has not changed since 5691f36 (2026-06-30). On 2026-10-05 I ran the reproduction again on dev at 907b3bc. The core tool still reported one replacement and left the file unchanged.

    #46849 fixes the core tool on dev, and #47088 fixes the v2 copy at packages/core/src/tool/plugin/edit.ts. The cleanup bot closed both PRs, and I asked for a reopen on each.

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

Metadata

Metadata

Assignees

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