Skip to content

fix(file_search): honor cancel_token to keep stop button responsive#2044

Closed
h3c-hexin wants to merge 1 commit into
Hmbown:mainfrom
h3c-hexin:pr/fix-file-search-cancel-token
Closed

fix(file_search): honor cancel_token to keep stop button responsive#2044
h3c-hexin wants to merge 1 commit into
Hmbown:mainfrom
h3c-hexin:pr/fix-file-search-cancel-token

Conversation

@h3c-hexin
Copy link
Copy Markdown
Contributor

Problem

FileSearchTool::execute is async fn but search_files() synchronously iterates the directory tree via ignore::WalkBuilder. On large trees ($HOME, mounted network volumes, deep node_modules/), this blocks the tokio task for tens of seconds. While the walker runs the engine cannot observe the shared CancellationToken, so Op::Cancel and frontend stop buttons are effectively no-ops until the walk completes. Observed in a wrapping GUI: the stop button was unresponsive for 90+ seconds when the model ran file_search with workspace=$HOME.

Fix

grep_files already solved this in search.rs by polling ToolContext.cancel_token between iterations and returning a ToolError on cancel. This PR applies the same pattern to file_search:

  • Thread cancel_token through search_files().
  • Call check_cancelled() once per WalkBuilder entry — cheap, returns fast, lets Op::Cancel actually stop the walk.
  • Reuse search.rs::check_cancelled (promoted to pub(super)) rather than duplicating the 5-line helper.
  • Add test_file_search_respects_cancel_token paralleling the existing test_grep_files_respects_cancel_token.

Relationship to #1790

This supersedes #1790 (closed as stale post v0.8.41 rebrand). That PR wrapped the walker in spawn_blocking with a 30s hard timeout. The gemini reviewer on #1790 correctly flagged that approach as the wrong shape:

Feedback suggests utilizing the existing CancellationToken from ToolContext to allow for immediate cancellation and avoid orphaned background threads.

This patch follows that guidance:

  • No spawn_blocking — the walker still runs on the async task, but yields cancel responsiveness via the explicit polling points (same as grep_files).
  • No orphan threads — check_cancelled() returns an error that propagates up cleanly.
  • No arbitrary timeout — cancellation is immediate when the token fires.

Tests

  • New: tools::file_search::tests::test_file_search_respects_cancel_token — pre-cancelled token returns an error containing "cancelled".
  • All 7 file_search tests + all 9 grep_files tests pass locally.

Diff stat

crates/tui/src/tools/file_search.rs | 33 +++++++++++++++++++++++++++++++--
crates/tui/src/tools/search.rs      |  2 +-
2 files changed, 32 insertions(+), 3 deletions(-)

`FileSearchTool::execute` is async but `search_files()` synchronously
iterates the directory tree via `ignore::WalkBuilder`. On large trees
($HOME, mounted volumes, deep node_modules/), this blocks the tokio task
for tens of seconds — `Op::Cancel` and the frontend stop button are
no-ops until the walk completes.

`grep_files` already solved this in search.rs by polling
`ToolContext.cancel_token` between iterations. This applies the same
pattern to `file_search`: thread the token through `search_files()` and
call `check_cancelled()` once per `WalkBuilder` entry. The shared helper
is promoted to `pub(super)` so the two sibling modules can reuse it.

This supersedes Hmbown#1790 (spawn_blocking + 30s timeout), which the gemini
reviewer correctly flagged as the wrong shape — the CancellationToken
approach leaves no orphan threads and surfaces cancellation immediately.
Copy link
Copy Markdown
Contributor

@gemini-code-assist gemini-code-assist Bot left a comment

Choose a reason for hiding this comment

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

Code Review

This pull request introduces cancellation support to the file search tool by integrating a CancellationToken into the search process and verifying it with a new test case. The review feedback highlights that the search operation is still synchronous and blocking, which could lead to executor starvation on large directory trees; it is recommended to offload this task using tokio::task::spawn_blocking.

Comment on lines +91 to +98
let matches = search_files(
query,
&base_path,
extensions,
exclude_patterns,
limit,
context.cancel_token.as_ref(),
)?;
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.

medium

While adding the cancel_token makes the search interruptible, the search_files function is still a synchronous, blocking operation. On large directory trees, this will block the Tokio worker thread for tens of seconds (as noted in the PR description). This can lead to latency spikes for other async tasks or even executor starvation.

Consider wrapping this call in tokio::task::spawn_blocking to offload the heavy I/O and iteration to a dedicated thread pool, while still passing the cancel_token to ensure the background thread exits promptly upon cancellation.

Hmbown added a commit that referenced this pull request May 25, 2026
Includes cancellable file_search/list_dir behavior, deterministic sub-agent whale-species nicknames, and an honest /balance command scaffold for the provider-billing work. Reviewed the overlapping file-search cancellation contribution in #2044 from @h3c-hexin; that PR's Windows failure was unrelated to the cancellation code, and the contributor context is preserved here.
@Hmbown
Copy link
Copy Markdown
Owner

Hmbown commented May 25, 2026

Thanks @h3c-hexin for the clear repro and the cancellation-token patch. The file_search stop/cancel fix landed through #2035 (merge commit d22da53) in the v0.8.45 control-plane slice, with cancellation checks plus blocking-worker isolation/timeout so long walks do not pin the async runtime.\n\nI also checked the #2044 Windows failure before closing this: it timed out in unrelated tools::js_execution tests after Linux/macOS/lint/npm smoke had passed, so I treated the contribution as superseded rather than rejected. Appreciate the focused writeup and test shape here.

@Hmbown Hmbown closed this May 25, 2026
Hmbown added a commit that referenced this pull request May 25, 2026
- Update @reidliu41 entry to add user-message transcript highlight (#1995)
- Update @zlh124 entry to add SKILL.md YAML block-scalar parser (#1908)
- Update @h3c-hexin entry to add file_search cancellation report (#2044)
- Add @lpeng1711694086-lang for user-message TUI contrast feature request (#1672)
Hmbown added a commit that referenced this pull request May 25, 2026
- Update @reidliu41 entry to add user-message transcript highlight (#1995)
- Update @zlh124 entry to add SKILL.md YAML block-scalar parser (#1908)
- Update @h3c-hexin entry to add file_search cancellation report (#2044)
- Add @lpeng1711694086-lang for user-message TUI contrast feature request (#1672)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants