Skip to content

fix(agents): show fuzzy match suggestions when edit oldText not found#42265

Closed
imwyvern wants to merge 2 commits into
openclaw:mainfrom
imwyvern:fix/edit-fuzzy-match-suggestions
Closed

fix(agents): show fuzzy match suggestions when edit oldText not found#42265
imwyvern wants to merge 2 commits into
openclaw:mainfrom
imwyvern:fix/edit-fuzzy-match-suggestions

Conversation

@imwyvern

Copy link
Copy Markdown
Contributor

Problem

When the edit tool fails to find oldText in a file, it returns:

Could not find the exact text in {path}. The old text must match exactly including all whitespace and newlines.

This is a dead-end — the model has no information about what's actually in the file. It often retries with the same wrong text, re-reads the entire file (wasting tokens), or gives up after multiple failed attempts.

Solution

Added a new wrapper wrapEditToolWithFuzzyMatchSuggestions that enriches "not found" errors with the closest matching regions from the file. Applied to both createHostWorkspaceEditTool and createSandboxedEditTool.

Algorithm: Sliding window across file lines scored with normalized LCS ratio. A pre-filter (8-char substring check) eliminates 90%+ of windows cheaply before full LCS computation.

Performance: Zero overhead on success — the wrapper only activates on failure. File size cap (100KB), binary detection, output cap (2000 chars), and early termination on near-perfect matches (>95%).

Example: Improved Error Output

Could not find the exact text in src/foo.ts.

The 2 most similar regions in the file:

--- Lines 42-47 (similarity: 87%) ---
     40 │ // handler setup
     41 │ const config = {};
>    42 │ function handleClick(event: MouseEvent) {
>    43 │   const target = event.target as HTMLElement;
>    44 │   if (target.classList.contains("selected")) {
>    45 │     target.remove();
>    46 │   }
>    47 │ }
     48 │
     49 │ // next section

--- Lines 128-133 (similarity: 72%) ---
>   128 │ function handleTouch(event: TouchEvent) {
>   129 │   const target = event.target as HTMLElement;
>   130 │   if (target.classList.contains("active")) {
>   131 │     target.remove();
>   132 │   }
>   133 │ }

Hint: Compare the shown regions with your oldText. Check for whitespace
differences, renamed variables, or recent file changes.

Test Coverage (24 tests)

  • Core algorithm: lcsLength, lcsRatio, scoreWindows — identity, empty, partial, no-match cases
  • Wrapper integration: success passthrough, non-not-found error passthrough, enriched errors with suggestions, binary file detection, large file handling, old_string param alias, no-similar-regions fallback, multiple ranked regions, path resolution

@openclaw-barnacle openclaw-barnacle Bot added agents Agent runtime and tooling size: L labels Mar 10, 2026

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 52f018b3bd

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread src/agents/pi-tools.host-edit.ts Outdated
@greptile-apps

greptile-apps Bot commented Mar 10, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR adds a wrapEditToolWithFuzzyMatchSuggestions wrapper that enriches "oldText not found" errors with the closest matching regions from the file, giving the model actionable context to self-correct in one shot instead of retrying blindly or re-reading the whole file. The wrapper is applied to both createSandboxedEditTool and createHostWorkspaceEditTool, with 24 tests covering core algorithm correctness and integration scenarios.

Issues identified:

  1. Pre-filter defeats its own purpose for indentation mismatches — The 8-char substring check includes leading whitespace, so when oldText has different indentation than the file (tabs vs spaces, or different depth), every line fails the pre-filter silently. This is the single most common reason oldText fails to match, meaning the feature would produce no suggestions in its most important use case.

  2. Full file read before the size cap checkfs.readFile loads the entire file into memory before content.length > FUZZY_MAX_FILE_SIZE_BYTES rejects it. A large binary or generated file (e.g. a 50 MB bundle) will be fully buffered on every failed edit call. Using fs.stat() before reading would honour the intended performance cap.

  3. Unexpected FS errors leak from the fallback read — If fs.readFile throws inside the inner try block (race condition, permission change), the FS error is rethrown to the agent instead of the original "not found" error, which is strictly more useful and less confusing.

Confidence Score: 2/5

  • The feature is functionally safe (failures degrade gracefully to the original error), but three implementation bugs substantially reduce its value and violate stated performance guarantees.
  • The feature is well-intentioned and well-structured, with comprehensive test coverage. However, it contains three functional issues that collectively undermine its core value proposition: (1) the indentation mismatch bug means the feature silently produces no suggestions in the most common real-world failure case; (2) the full-file-read-before-size-check violates the performance cap described in the PR; (3) the error handling edge case surfaces confusing filesystem errors instead of the original context. These should be addressed before merging.
  • src/agents/pi-tools.host-edit.ts — the pre-filter logic (lines 152–169), the size check ordering (lines 303–321), and error handling (lines 327–332) all need fixes before the feature behaves as described.

Last reviewed commit: 52f018b

Comment thread src/agents/pi-tools.host-edit.ts Outdated
Comment thread src/agents/pi-tools.host-edit.ts Outdated
Comment thread src/agents/pi-tools.host-edit.ts
@imwyvern
imwyvern force-pushed the fix/edit-fuzzy-match-suggestions branch from 52f018b to b45f11d Compare March 10, 2026 15:15
@imwyvern

Copy link
Copy Markdown
Contributor Author

Addressed all 4 review items in b45f11d:

  1. P1 sandbox path: Removed fuzzy wrapper from createSandboxedEditTool — the wrapper uses host fs.readFile/fs.stat which can't access sandbox-only paths
  2. Large file perf: Now uses fs.stat() to check size before reading, so 50MB bundles never load into memory
  3. Indentation pre-filter: Added trimStart() before 8-char substring check so whitespace-only differences don't defeat the filter
  4. FS error fallback: Changed inner catch to only surface errors with cause === err (our enriched throws), all unexpected FS errors fall back to the original "not found" error

24/24 tests pass, lint clean.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: b45f11d1c6

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread src/agents/pi-tools.host-edit.ts Outdated
When the edit tool fails to find oldText in a file, the error message
is a dead-end — the model has no information about what's actually in
the file and often retries blindly or re-reads the entire file.

This adds a new wrapper `wrapEditToolWithFuzzyMatchSuggestions` that
enriches not-found errors with the closest matching regions from the
file, including line numbers and similarity scores. The model can then
self-correct in one shot.

Algorithm:
- Sliding window across file lines with LCS ratio scoring
- Pre-filter eliminates 90%+ of windows cheaply
- Returns top 3 matches above 40% similarity threshold

Performance:
- Zero overhead on success (wrapper only activates on failure)
- File size cap at 100KB, binary file detection
- Output capped at 2000 chars
- Early termination on near-perfect (>95%) match

Applied to both createHostWorkspaceEditTool and createSandboxedEditTool.
@imwyvern
imwyvern force-pushed the fix/edit-fuzzy-match-suggestions branch from b45f11d to 73d0392 Compare March 10, 2026 15:34
@imwyvern

Copy link
Copy Markdown
Contributor Author

Fixed P1 prefilter issue in 73d0392:

  • Extracted hasSharedPreFilterChunk() helper — now samples 3 chunks (start/middle/end) across each line instead of only the first 8 chars, so prefix renames (const→let) are no longer filtered out
  • Single-line oldText skips prefilter entirely — most common edit failure case now always reaches LCS scoring
  • Added 2 regression tests: multi-line prefix change + single-line rename

26/26 tests pass, lint clean.

@imwyvern

Copy link
Copy Markdown
Contributor Author

Fixed P1 prefilter issue in 73d0392: Extracted hasSharedPreFilterChunk() that samples prefix/middle/suffix 8-char chunks instead of only the first 8 chars. Renamed leading tokens (constlet) now survive the prefilter. Added 2 regression tests (26/26 pass).

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 645faa2961

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread src/agents/pi-tools.host-edit.ts
@imwyvern
imwyvern force-pushed the fix/edit-fuzzy-match-suggestions branch from 645faa2 to e5766d1 Compare March 10, 2026 16:52
@imwyvern

Copy link
Copy Markdown
Contributor Author

Fixed P2 short-line prefilter in e5766d1: Short lines (<8 chars) now always pass the prefilter — they're cheap to LCS-score anyway, and the old includes() fallback was too strict for single-char differences (let x vs const x). Added regression test. 27/27 tests pass.

@imwyvern
imwyvern force-pushed the fix/edit-fuzzy-match-suggestions branch from e5766d1 to 7decafb Compare March 11, 2026 01:23
@imwyvern
imwyvern force-pushed the fix/edit-fuzzy-match-suggestions branch from 7decafb to e5766d1 Compare March 11, 2026 01:25

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: e5766d1ac3

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread src/agents/pi-tools.read.ts Outdated
Comment thread src/agents/pi-tools.host-edit.ts Outdated
@imwyvern
imwyvern force-pushed the fix/edit-fuzzy-match-suggestions branch from e5766d1 to a8212f3 Compare March 11, 2026 02:18
Add explicit tests verifying the three review concerns:
- fs.stat() is called before fs.readFile() (no full read before size check)
- Pre-filter trims leading whitespace (indentation mismatches not rejected)
- FS errors in fallback rethrow original error (not raw ENOENT)

All three fixes were already present in the implementation; these tests
document the expected behavior for each review point.
@imwyvern
imwyvern force-pushed the fix/edit-fuzzy-match-suggestions branch from a8212f3 to 4b8d1d6 Compare March 11, 2026 02:40
@imwyvern

Copy link
Copy Markdown
Contributor Author

Closing to reduce active PR count (10-PR limit). Will reopen if queue clears. The branch and code are preserved.

@imwyvern imwyvern closed this Mar 16, 2026
imwyvern pushed a commit to imwyvern/openclaw that referenced this pull request May 24, 2026
…head

When edit oldText doesn't match exactly, the error hint now locates and
displays the most similar region in the file (with line numbers) instead
of dumping the first 800 characters. This helps the model quickly spot
which lines diverged from its expectation.

Falls back to the current full-file-head behavior when no region scores
above zero shared lines.

Combines the intent of openclaw#56857 and openclaw#42265.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

agents Agent runtime and tooling size: L

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant