{"record":{"id":"c5122cc14533b582","repo":"can1357/oh-my-pi","slug":"refusing-partial-line-match-in-path-at-line-m","errorCode":null,"errorMessage":"Refusing partial-line match in ${path} at line ${matchStartIndex + j + 1}: the file line also contains ${JSON.stringify(part)}, which the replacement would silently drop. Provide the complete line in the hunk.","messagePattern":"Refusing partial-line match in (.+?) at line (.+?): the file line also contains (.+?), which the replacement would silently drop\\. Provide the complete line in the hunk\\.","errorType":"validation","errorClass":"ApplyPatchError","httpStatus":null,"severity":"error","filePath":"packages/coding-agent/src/edit/modes/patch.ts","lineNumber":1075,"sourceCode":"\tpath: string,\n\tpattern: string[],\n\tmatchedLines: string[],\n\tnewLines: string[],\n\tmatchStartIndex: number,\n): void {\n\tlet newLinesNorm: string | undefined;\n\tfor (let j = 0; j < pattern.length; j++) {\n\t\tconst lineNorm = normalizeForFuzzy(matchedLines[j]);\n\t\tconst patternNorm = normalizeForFuzzy(pattern[j]);\n\t\tif (lineNorm === patternNorm) continue;\n\t\tconst at = lineNorm.indexOf(patternNorm);\n\t\tif (at === -1) continue;\n\t\tconst discardedParts = [lineNorm.slice(0, at).trim(), lineNorm.slice(at + patternNorm.length).trim()];\n\t\tfor (const part of discardedParts) {\n\t\t\tif (part.length === 0) continue;\n\t\t\tnewLinesNorm ??= newLines.map(normalizeForFuzzy).join(\"\\n\");\n\t\t\tif (!newLinesNorm.includes(part)) {\n\t\t\t\tthrow new ApplyPatchError(\n\t\t\t\t\t`Refusing partial-line match in ${path} at line ${matchStartIndex + j + 1}: ` +\n\t\t\t\t\t\t`the file line also contains ${JSON.stringify(part)}, which the replacement would silently drop. ` +\n\t\t\t\t\t\t`Provide the complete line in the hunk.`,\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\t}\n}\n\n/**\n * Compute replacements needed to transform originalLines using the diff hunks.\n */\nfunction computeReplacements(\n\toriginalLines: string[],\n\tpath: string,\n\thunks: DiffHunk[],\n\tallowFuzzy: boolean,\n): { replacements: Replacement[]; warnings: string[] } {","sourceCodeStart":1057,"sourceCodeEnd":1093,"githubUrl":"https://github.com/can1357/oh-my-pi/blob/969062200754ea02cfac922e5ebb8c608c079e15/packages/coding-agent/src/edit/modes/patch.ts#L1057-L1093","documentation":"During a fuzzy/substring sequence match, the matched hunk lines covered only part of one or more whole file lines (assertPartialMatchPreservesDiscardedText). Applying a whole-line replacement would delete the extra text that was in the file line but absent from the hunk, so the patcher refuses instead of silently dropping it. The fix is to include the complete file line(s) in the hunk's removed lines.","triggerScenarios":"A hunk's old lines match a substring/prefix of a longer file line (e.g. the model wrote `foo = 1` while the file has `public foo = 1;`), and the leftover text (before or after the matched part) does not appear anywhere in the hunk's new lines.","commonSituations":"Model abbreviated a line it saw truncated; file line has decorators, visibility modifiers, or trailing comments the hunk omitted; tab/space normalization partially aligns a long line; editing minified or unusually long single-line code.","solutions":["Re-read the file at the reported line and copy the complete line verbatim into the hunk's removed lines.","Add the missing remainder text to the replacement lines so nothing is dropped.","Increase surrounding context in the hunk so an exact whole-line match is found instead of a substring strategy.","Reduce reliance on fuzzy matching: provide the exact original line including whitespace/modifiers."],"exampleFix":"// before (hunk line: 'foo = 1' matching file line 'public foo = 1; // count')\n-  foo = 1\n+  foo = 2\n\n// after\n-  public foo = 1; // count\n+  public foo = 2; // count","handlingStrategy":"validation","validationCode":"const fileLines = (await Bun.file(path).text()).split('\\n');\nconst target = fileLines[lineNo - 1] ?? '';\nif (!hunk.oldLines.some(l => l.trim() === target.trim())) {\n  // file line is longer than hunk line: copy the full file line into the hunk\n  hunk.oldLines = [target];\n}","typeGuard":null,"tryCatchPattern":"try {\n  applyPatch(patch);\n} catch (err) {\n  if (err instanceof ApplyPatchError && err.message.includes('Refusing partial-line match')) {\n    // re-read file at the reported line and rebuild the hunk with complete lines\n  } else throw err;\n}","preventionTips":["Copy removed lines verbatim from a fresh file read — never abbreviate.","Include modifiers, comments, and trailing punctuation in hunk lines.","Prefer exact-match hunks with ample context over minimal fuzzy fragments.","Avoid patching very long single-line or minified regions."],"tags":["patch","fuzzy-match","data-loss-protection"],"backgroundTag":"patch-partial-line-match-refused","analyzedSha":"969062200754ea02cfac922e5ebb8c608c079e15","analyzedAt":"2026-08-31T10:29:35.737Z","schemaVersion":2},"datasetVersion":"2026-08-31T14:17:45.589Z"}