apply_patch verification fails on canonically-equivalent Unicode — seekSequence needs an NFC pass
Nobody has claimed this yet.
Assessment
- Difficulty
- 2/5
- Estimated time
- 1-3 hours
- Newbie friendliness
- 78/100
- Issue type
- Bug
- Clarity
- Clearly specified
- Activity status
- Quiet
- Tech stack
- typescript
- Domain
- tooling
Research direction
Start at packages/opencode/src/patch/index.ts:460, tracing seekSequence and the deriveNewContentsFromChunks entry point. Run the deterministic NFD/NFC reproduction from the issue, then add focused coverage for canonically equivalent lines alongside the existing patch tests. Done means an NFC context matches NFD file content without changing non-equivalent matching behavior.
Written by the indexing model from the issue text.
Description
Description
apply_patch fails with Failed to find expected lines in <file> when the file on disk and the model's context lines are canonically-equivalent Unicode but different byte sequences (NFC vs NFD). None of the 4 passes in seekSequence (packages/opencode/src/patch/index.ts:460) handle this:
- exact
- rstrip
- trim
normalizeUnicode+ trim — butnormalizeUnicode(index.ts:418) only maps smart quotes, dashes, ellipsis and NBSP; it never touches combining characters.
So a file containing decomposed й (и U+0438 + U+0306) can never be matched by a patch containing precomposed й (U+0439), even though the strings are canonically equivalent and render identically. Models essentially always emit NFC, so any NFD content on disk makes the file permanently unpatchable via apply_patch — the model re-reads, retries, fails again, and burns tokens in a loop.
We hit this in production on Cyrillic markdown files (tables with rows like | Район | Астана | ... |). Related-but-different reports: #2904 (auto-closed stale), #31422 (formatter drift, closed not-planned), #27282/#11687 (Windows). None cover canonical equivalence.
Proposed fix
A 5th pass in seekSequence, after the current pass 4:
// Pass 5: NFC-normalized match (canonical equivalence — no false positives)
const nfc = tryMatch(
lines, pattern, startIndex,
(a, b) => a.normalize("NFC").trim() === b.normalize("NFC").trim(),
eof,
)
return nfc
This is safe: NFC normalization followed by exact equality only matches strings Unicode defines as the same text. Happy to send a PR with this + tests if you'd take it.
Plugins
None
OpenCode version
1.15.4 (gap still present at current HEAD of packages/opencode/src/patch/index.ts)
Steps to reproduce
Engine-level (deterministic, no model needed):
import { Patch } from "./packages/opencode/src/patch" // deriveNewContentsFromChunks
const fileContent = "# Сводка\nРайон: Астана\n".normalize("NFD") // й stored decomposed
const chunk = {
old_lines: ["Район: Астана".normalize("NFC")], // models emit NFC
new_lines: ["Район: Алматы"],
is_end_of_file: false,
}
// -> throws: Failed to find expected lines
End-to-end:
node -e 'require("fs").writeFileSync("test.md", "# Сводка\nРайон: Астана\n".normalize("NFD"))'- Ask any model to change
АстанаtoАлматыintest.mdusingapply_patch. apply_patch verification failed: Failed to find expected lines in test.md— and every retry fails the same way, since re-reading doesn't change the canonical-equivalence mismatch.
Operating System
Linux (Debian-based container)
Terminal
headless (server)
- Dominant language
- TypeScript
- Stars
- 209k
- Forks
- 27.5k
- Avg merge
- 7h 20m
- Merged PRs (30d)
- 358
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
More from anomalyco/opencode
-
Difficulty 2/5 1-3 hours Newbie friendliness 76/100
-
[FEATURE]: Bump gitlab-ai-provider to 6.15.1 on v2 (Fable 5.1, GPT-6 Astra, workflow reconnect fix) Open
Difficulty 1/5 Under an hour Newbie friendliness 90/100
-
Difficulty 2/5 1-3 hours Newbie friendliness 88/100
-
Difficulty 1/5 Under an hour Newbie friendliness 88/100
-
Difficulty 2/5 1-3 hours Newbie friendliness 78/100
All issues in anomalyco/opencode
Similar issues
-
calcite-components needs triage refactor
Difficulty 2/5 1-3 hours Newbie friendliness 75/100
Esri/calcite-design-system#15203 ·
-
Difficulty 2/5 1-3 hours Newbie friendliness 91/100
-
community first-timers-only good first issue hacktoberfest help wanted low hanging fruit up-for-grabs
Difficulty 1/5 Under an hour Newbie friendliness 95/100
-
Difficulty 2/5 1-3 hours Newbie friendliness 78/100
Automattic/studio#4908 ·
-
Difficulty 2/5 1-3 hours Newbie friendliness 90/100