Skip to content

@-picker freezes the CLI for on large workspaces #3454

@callmeYe

Description

@callmeYe

What happened?

When typing @ inside the interactive CLI to open the file picker, the main thread is frozen for the entire duration of the initial file crawl. On a medium-sized workspace (~3k files) the stall is around 100–150 ms; on a home directory with ~100k files the CLI becomes unresponsive for several seconds (keystrokes drop, cursor stops blinking, Ink stops rendering).

Root cause traces to two synchronous pieces of work on the main thread:

  1. fdir recursive crawl in packages/core/src/utils/filesearch/crawler.ts.
  2. new AsyncFzf(allFiles, ...) in packages/core/src/utils/filesearch/fileSearch.ts:199 — the Async prefix is misleading; construction is synchronous and dominates for large file counts.

Also, the 200 ms "Loading suggestions…" timer in useAtCompletion.ts only covers search() and not initialize(), so during the freeze there's no visual feedback at all — the CLI just appears stuck.

What did you expect to happen?

The @ picker should never block the render loop. On a cold start against a large tree it's OK to show a loading indicator for a moment, but the prompt, cursor, and other UI should remain responsive the whole time. On subsequent presses results should appear instantly.

Client information

Client Information
Qwen Code v0.14.5
macOS 15.1.1 (24B2091, arm64)
Node.js v22.17.0

Bug is reproducible from git main at 30c5eeaf2.

Login information

N/A — bug is in the client UI, independent of auth.

Anything else we need to know?

Repro: QWEN_WORKING_DIR=~ npm start (or just run qwen from ~), then type @.

I've pushed a WIP fix to my fork that moves the crawl + fzf index into a worker_threads worker: https://github.com/callmeYe/qwen-code/tree/feat/filesearch-worker-p1. Happy to open a PR once the maintainers confirm the direction.

Key observations from that work:

  • Empirically on macOS: fdir is faster than ripgrep via child_process for small/medium trees (<50k files) because spawn+IPC overhead dominates, but ripgrep wins 3-4× past ~100k files.
  • A persistent file-index cache isn't currently necessary — moving the work off the main thread is enough to restore responsiveness on typical workspaces.

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

No type
No fields configured for issues without a type.

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions