[BUG] VS Code extension: ripgrep resolver discards its own probe and returns bare `rg`, silently degrading every @-mention search to a 100-result unranked fallback

Status Open
Reported on v2.1.266
Maintainer reply None cached
Activity 1 comment · opened Sep 9, 2026

Summary

In the VS Code/Cursor extension, @-mention file search has a ripgrep path and a vscode.workspace.findFiles fallback. The ripgrep resolver runs a probe, discards the result, and returns the bare name rg regardless. When ripgrep isn't on PATH — the default state on macOS, where neither the OS nor VS Code/Cursor puts one there — execFile("rg", …) fails with ENOENT on every search, and the extension silently falls back to a path that is badly degraded.

Net effect: on any workspace larger than ~100 files, @-mention search doesn't find files that plainly exist, and doesn't find folders. Under 100 files it looks fine, so it reads to users as "search is broken on large codebases."

Root cause

extension.js (v2.1.266, ~offset 3,096,471):

var t$$ = 100, fP0 = 20000000, yP0 = 1e4,
gP0 = g3(() => {
  let $ = process.platform === "win32" ? "rg.exe" : "rg";
  try { nT.execFileSync($, ["--version"], { stdio: "ignore" }) } catch {}
  return $                                    // <-- probe result discarded
})

The try/catch is dead code: the return value is the bare name whether or not the probe succeeded. There is no fallback to the @vscode/ripgrep binary the host editor already ships.

The consumer (~offset 3,131,271):

let G;
try   { G = await su$($, this.cwd, SQ(this.output)) }   // ripgrep
catch (V) {
  this.output.warn(`Ripgrep search failed, falling back to VSCode findFiles: ${V}`);
  G = await S$$($)                                       // degraded fallback
}

The fallback S$$ is materially worse than the ripgrep path:

Y = await q5.workspace.findFiles(Q, J, 100);   // hard 100-result cap
…
let K = new Set;
for (let G of W) { let V = G.indexOf("/"); while (V !== -1) K.add(G.substring(0,V)), V = G.indexOf("/", V+1) }
let U = $?.toLowerCase();
for (let G of K) if (!U || G.toLowerCase().includes(U)) z.push({ path: G+"/", … type: "directory" });
z.sort((G, V) => G.path.localeCompare(V.path));  // alphabetical, no relevance ranking
  • 100-result hard cap — an arbitrary slice of a large repo.
  • Folders are derived only from those ≤100 files, so a folder appears only if it happens to be an ancestor of one of them.
  • No fuzzy ranking at all — results are localeCompare-sorted, unlike the ripgrep path.

By contrast su$ lists the whole tree, synthesizes folder entries at every depth (hP0), and ranks with Fuse.js. The fallback was clearly meant to be rare; because of the resolver bug it is the only path for users without ripgrep on PATH.

Reproduction

  1. On macOS with no ripgrep on PATH (command -v rg → nothing; note a shell alias/function does not count, since execFile doesn't use a shell).
  2. Open a workspace with more than ~100 files.
  3. Type @ and search for a file that sorts late in the walk, or for any folder name.
  4. The file/folder is not offered, though it exists.
  5. The extension's output channel shows Ripgrep search failed, falling back to VSCode findFiles: … ENOENT.

Confirming the resolver is the cause:

$ node -e 'require("child_process").execFile("rg",["--version"],(e,o)=>console.log(e?"ERR "+e.code:"OK "+o))'
ERR ENOENT

Verification script

This evaluates the real shipped resolver out of the installed bundle and exercises it against a 301-file tree:

const fs=require('fs'),os=require('os'),path=require('path'),cp=require('child_process');
const src=fs.readFileSync(process.argv[2],'utf8');
const m=src.match(/gP0=g3\(\(\)=>\{.*?return \$\}\)/s);
const resolve=new Function('g3','nT','e$$','$4$',`let ${m[0]}; return gP0;`)(
  f=>f, cp, path, {env:{appRoot:'/Applications/Cursor.app/Contents/Resources/app'}});
const rg=resolve();
console.log('resolved rg =',rg);
console.log(cp.execFileSync(rg,['--version'],{encoding:'utf8'}).split('\n')[0]);

With no ripgrep on PATH:

resolved rg = rg
Error: spawn rg ENOENT

…even though <appRoot>/node_modules/@vscode/ripgrep/bin/rg exists and is executable.

Suggested fix

Honor the probe the resolver already performs, and fall back to the host editor's bundled ripgrep. PATH stays first, so behavior is unchanged for anyone who has ripgrep installed — this only adds recovery:

gP0 = g3(() => {
  let $ = process.platform === "win32" ? "rg.exe" : "rg", Q = [$];
  try {
    let J = $4$.env.appRoot;
    if (J) Q.push(
      e$$.join(J, "node_modules", "@vscode", "ripgrep", "bin", $),
      e$$.join(J, "node_modules.asar.unpacked", "@vscode", "ripgrep", "bin", $));
  } catch {}
  for (let J of Q) try { nT.execFileSync(J, ["--version"], { stdio: "ignore" }); return J } catch {}
  return $
})

Verified locally: this resolves …/Cursor.app/Contents/Resources/app/node_modules/@vscode/ripgrep/bin/rg (ripgrep 15.1.0) and @-mention search returns correct files and folders again. Installing ripgrep with brew install ripgrep independently fixes it too, which further confirms the diagnosis.

Two things worth considering alongside the resolver fix:

  • The fallback silently produces low-quality results. Since it can't be made as good as ripgrep, it may be worth surfacing the degradation in the UI rather than only in the output channel.
  • Separately, uP0 runs ripgrep with a 10s timeout and 20MB maxBuffer, and on SIGTERM / ERR_CHILD_PROCESS_STDIO_MAXBUFFER it resolves with partial output instead of rejecting:

``js
if ((K || U) && W) { let G = z.trim().split("\n").filter(Boolean); J(G.length > 0 ? G.slice(0,-1) : []); return }
``

On a genuinely large monorepo that yields a silently truncated index with no warning and no fallback. Possibly related to #77488.

Adjacent but distinct from #83097 (glob relativization) and #87827 (multi-root workspaces) — both of those assume the ripgrep path is running at all.

Environment

  • Extension: anthropic.claude-code 2.1.266 (darwin-arm64)
  • Host: Cursor 3.19.13 (same resolver code is host-agnostic; not separately verified on VS Code)
  • macOS 26.4 (arm64)
  • Bundled ripgrep present at <appRoot>/node_modules/@vscode/ripgrep/bin/rg (15.1.0), unused

View original on GitHub ↗

This issue has 1 comment on GitHub. Read the full discussion on GitHub ↗