From 52ffb98453c0fc449760c978eb0cfe152bac32cc Mon Sep 17 00:00:00 2001 From: 32134178csa <321dsadsaca@protonmail.com> Date: Fri, 27 Mar 2026 21:46:37 -0700 Subject: [PATCH] fix(filesystem): improve edit_file error message with nearest-match diagnostics When edit_file fails to find a match, the error now shows the closest near-match location with line numbers, matching percentage, and a line-by-line diff highlighting which lines diverge. This helps agents and users quickly identify whitespace or content mismatches. Fixes #2034 --- src/filesystem/__tests__/lib.test.ts | 6 ++--- src/filesystem/lib.ts | 38 +++++++++++++++++++++++++++- 2 files changed, 40 insertions(+), 4 deletions(-) diff --git a/src/filesystem/__tests__/lib.test.ts b/src/filesystem/__tests__/lib.test.ts index f7e585af22..efb47ebeac 100644 --- a/src/filesystem/__tests__/lib.test.ts +++ b/src/filesystem/__tests__/lib.test.ts @@ -494,13 +494,13 @@ describe('Lib Functions', () => { ); }); - it('throws error for non-matching edits', async () => { + it('throws error for non-matching edits with diagnostic info', async () => { const edits = [ { oldText: 'nonexistent line', newText: 'replacement' } ]; - + await expect(applyFileEdits('/test/file.txt', edits, false)) - .rejects.toThrow('Could not find exact match for edit'); + .rejects.toThrow('Could not find match for edit'); }); it('handles complex multi-line edits with indentation', async () => { diff --git a/src/filesystem/lib.ts b/src/filesystem/lib.ts index 17e4654cd5..aa07b389f5 100644 --- a/src/filesystem/lib.ts +++ b/src/filesystem/lib.ts @@ -248,7 +248,43 @@ export async function applyFileEdits( } if (!matchFound) { - throw new Error(`Could not find exact match for edit:\n${edit.oldText}`); + // Find the closest near-match to help diagnose the mismatch + const oldLines = normalizedOld.split('\n'); + const contentLines = modifiedContent.split('\n'); + let bestScore = 0; + let bestLineStart = 0; + + for (let i = 0; i <= contentLines.length - oldLines.length; i++) { + const potentialMatch = contentLines.slice(i, i + oldLines.length); + let score = 0; + for (let j = 0; j < oldLines.length; j++) { + if (oldLines[j].trim() === potentialMatch[j].trim()) { + score++; + } + } + if (score > bestScore) { + bestScore = score; + bestLineStart = i; + } + } + + let diagnostic = `Could not find match for edit in ${filePath}`; + if (bestScore > 0) { + const matchPct = Math.round((bestScore / oldLines.length) * 100); + diagnostic += `\nClosest match (${matchPct}% of lines) at lines ${bestLineStart + 1}-${bestLineStart + oldLines.length}:`; + const nearMatchLines = contentLines.slice(bestLineStart, bestLineStart + oldLines.length); + for (let j = 0; j < oldLines.length; j++) { + const marker = oldLines[j].trim() === nearMatchLines[j].trim() ? ' ' : '!'; + diagnostic += `\n ${marker} line ${bestLineStart + 1 + j}: ${JSON.stringify(nearMatchLines[j])}`; + if (marker === '!') { + diagnostic += `\n expected: ${JSON.stringify(oldLines[j])}`; + } + } + } else { + diagnostic += `\nNo similar content found in the file (${contentLines.length} lines).`; + } + diagnostic += `\n\nEdit oldText:\n${edit.oldText}`; + throw new Error(diagnostic); } }