Skip to content

fix(vscode): Prevent timeout in binary search for large pnpm monorepos#17580

Closed
MIreland wants to merge 3 commits intooxc-project:mainfrom
MIreland:fix/prevent-timeout-in-binary-search
Closed

fix(vscode): Prevent timeout in binary search for large pnpm monorepos#17580
MIreland wants to merge 3 commits intooxc-project:mainfrom
MIreland:fix/prevent-timeout-in-binary-search

Conversation

@MIreland
Copy link

@MIreland MIreland commented Jan 2, 2026

AI Disclaimer:

The code changes in this PR was written entirely by Cursor.

I opened this issue: #17578

Then I started debugging the extension locally, adding logs with cursor and tracking down where my plugin was failing to instantiate.

I walked through the entire process with cursor, feeding it logs and manually walking through several permutations of trial and error on my local instance of vscode. The code and traversal patterns themselves were written by AI.

I have a large local pnpm monorepo that does NOT work with the published vscode extension. It does work with this branch.

Problem

This is a fix for #17578

The VS Code extension was timing out when searching for binaries in large pnpm monorepos. The workspace.findFiles call with a recursive pattern **/node_modules/.bin/${defaultPattern} was searching through potentially thousands of nested node_modules directories, causing indefinite hangs.

Solution

Implemented a hybrid approach that prioritizes fast file system operations and only uses findFiles as a last resort with timeout protection:

  1. Caching: Results are cached to avoid repeated searches
  2. Fast path checks: Checks root node_modules/.bin/ in each workspace folder first
  3. Depth-limited file system search: Uses recursive file system operations (max depth 3) to search parent directories
  4. Timeout protection: Wraps findFiles with a 5-second timeout using Promise.race to prevent indefinite hanging

Changes

  • Added searchBinaryInNodeModules() method for depth-limited file system search
  • Added findFilesWithTimeout() helper method with timeout protection
  • Refactored searchBinaryPath() to use progressive search strategy
  • Added cache invalidation when workspace folders change

Performance Benefits

  • No indefinite hangs: Timeout ensures we never wait forever
  • Fast path: File system operations are much faster than findFiles in large monorepos
  • Graceful degradation: Falls back to findFiles only if needed, with timeout protection
  • Depth limiting: Prevents deep recursion in very large monorepos

Testing

  • Verified behavior remains the same for existing scenarios
  • Works in both npm/yarn and pnpm monorepos
  • Handles binaries in root, nested, and workspace-specific locations

Copilot AI review requested due to automatic review settings January 2, 2026 01:43
@MIreland MIreland requested a review from camc314 as a code owner January 2, 2026 01:43
@github-actions github-actions bot added A-editor Area - Editor and Language Server C-bug Category - Bug labels Jan 2, 2026
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This PR addresses timeout issues in the VS Code extension when searching for binaries in large pnpm monorepos by implementing a hybrid search strategy with caching, fast-path file system operations, depth-limited searching, and timeout protection.

Key Changes:

  • Implements a three-tier progressive search strategy: direct file system checks, depth-limited recursive search, and timeout-protected findFiles fallback
  • Adds binary path caching to avoid repeated expensive searches
  • Introduces timeout protection (5 seconds) for the workspace.findFiles API to prevent indefinite hangs

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

- Fix cache check to use Map.has() instead of !== undefined
- Clear cache when binary path settings change in onVscodeConfigChange
- Fix comment accuracy about search direction
- Fix misleading comment about deep recursion
- Make Windows drive letter regex case-insensitive
- Skip .pnpm directories in file system search
- Fix timeout leak by clearing timeout ID in finally block
- Improve error handling in catch block
@connorshea
Copy link
Member

Based on the PR description I presume this was - at least in large part - generated using AI tooling.

Please update the PR description to note any and all AI usage in accordance with our policy, preferably including models/tools used and a confirmation that you have personally reviewed the code generated and ensured it works/makes sense: https://oxc.rs/docs/contribute/introduction.html#ai-usage-policy

If the PR description is not updated in a timely manner, we will opt to close this PR to avoid maintainer fatigue and prevent spending too much time on contributions not thoroughly reviewed by the contributor.

@connorshea
Copy link
Member

connorshea commented Jan 2, 2026

It may also be worth checking #17345 to see if that fixes the problem for your large monorepo as an alternative, although it will mostly depend on Sysix/camc's preference here - assuming both fix the problem adequately.

@connorshea connorshea requested a review from Sysix January 2, 2026 01:59
@MIreland
Copy link
Author

MIreland commented Jan 2, 2026

@connorshea Added an AI disclaimer with context to the description.

@connorshea
Copy link
Member

@connorshea Added an AI disclaimer with context to the description.

Thank you!

pattern: RelativePattern,
exclude: string | null,
maxResults: number,
timeoutMs: number = 5000,
Copy link
Member

Choose a reason for hiding this comment

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

I know of some cases (mainly networked drives) where searching for files could take at least a few seconds, and so we may want to consider bumping this up a bit. We could make it configurable, but it's not my preference.

@MIreland
Copy link
Author

MIreland commented Jan 2, 2026

#17345 also fixes the issue if oxfmt is installed. I don't currently plan to use oxfmt, although I'll stick it in the node_modules if I need to to make the extension work 😂

I'll call this out in that PR as well.

@connorshea
Copy link
Member

although I'll stick it in the node_modules if I need to to make the extension work 😂

lol I guess that's okay as a temporary workaround until we ship a fix, but yeah it's definitely not intentional that the extension locks up for users with only oxlint installed

@connorshea
Copy link
Member

Oh also #17511 probably improves the handling of a missing oxfmt binary and has already been merged, will go out in the release on Monday.

@MIreland
Copy link
Author

MIreland commented Jan 2, 2026

At first glance it doesn't look like that code would help handle the situation when .findFiles times out, just the situations where it doesn't. But I'm happy to verify come Monday!

Copy link
Member

@Sysix Sysix left a comment

Choose a reason for hiding this comment

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

Thanks for working on this 🫶 Did you try to use tinyglobby npm package and avoid workspace.findFiles completely? I am confused why this is really slow.


// Skip .pnpm directories to avoid traversing into pnpm's internal structure
// Check if we're inside a .pnpm directory path
if (
Copy link
Member

Choose a reason for hiding this comment

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

I do not like it, that we have a special check for only .pnpm.
Can the search really ends up in a .pnpm directory?

}

// Check with .exe extension on Windows
if (process.platform === "win32") {
Copy link
Member

Choose a reason for hiding this comment

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

For windows, we do not use the .exe. Depending on the package manager, there is no exe.
That's why we use shell: true for windows, to target the same binary :)

: {
// On Windows with shell, quote the command path to handle spaces in usernames/paths
command: isWindows ? `"${path}"` : path,
args: ["--lsp"],
options: {
// On Windows we need to run the binary in a shell to be able to execute the shell npm bin script.
// Searching for the right `.exe` file inside `node_modules/` is not reliable as it depends on
// the package manager used (npm, yarn, pnpm, etc) and the package version.
// The npm bin script is a shell script that points to the actual binary.
// Security: We validated the user defined binary path in `configService.searchBinaryPath()`.
shell: isWindows,
env: serverEnv,
},

* Searches for a binary in node_modules/.bin directories using file system operations.
* This is much faster than workspace.findFiles in large monorepos.
* Limits search depth and scope to avoid timeouts.
* Note: This method searches upward from the start path toward the filesystem root.
Copy link
Member

Choose a reason for hiding this comment

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

I do not like this too. We have validateSafeBinaryPath which explicit disallow parent directory access.

maxResults: number,
timeoutMs: number = 5000,
): Promise<Uri[]> {
const searchPromise = workspace.findFiles(pattern, exclude, maxResults);
Copy link
Member

Choose a reason for hiding this comment

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

workspace.findFiles has a fourth parameter to cancel the search. Use this parameter instead of Promise.race :)

1,
// Check cache first
const cacheKey = `auto:${defaultPattern}`;
if (this.binaryPathCache.has(cacheKey)) {
Copy link
Member

Choose a reason for hiding this comment

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

searchBinaryPath will only be called at the start of the extension. Can you tell me why a cache is needed?

return result;
} catch (error) {
// Timeout or other error - log and cache undefined to avoid retrying
// Note: Error logging is intentionally minimal to avoid noise in production
Copy link
Member

Choose a reason for hiding this comment

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

can we add a window.showErrorMessage so the user knows the search failed?
Probably suggest setting the oxc.path.* configuration to avoid automatic search

Copy link
Author

Choose a reason for hiding this comment

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

I'm just going to shut this PR down in favor of #17345, which works also works for my giant monorepo.


// Fast path: Check common locations first using file system operations
// This is much faster than recursive findFiles in large monorepos
const workspaceFolders = Array.from(this.workspaceConfigs.keys());
Copy link
Member

Choose a reason for hiding this comment

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

The old implementation used only the first workspace folder. I guess this could lead to performance problems too. Specially with setups like in this example:
#17515

@MIreland
Copy link
Author

MIreland commented Jan 2, 2026

I'm going to close this in favor of #17345

@MIreland MIreland closed this Jan 2, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

A-editor Area - Editor and Language Server C-bug Category - Bug

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants