Skip to content

shell.resolveSpawnCommand walks PATH × PATHEXT with statSync on every spawn and picks Git's cmd launcher #11221

Description

@SkiTee3000

What happened

Every child process the server starts goes through resolveSpawnCommand, which re-scans PATH synchronously each time and, on Git for Windows, resolves git to the cmd\git.exe launcher. Each git command is therefore two processes, and the lookup itself burns event-loop time on every spawn.

Diagnosis

  • processRunner.ts:294 calls resolveSpawnCommand for every process. resolveSpawnExecutableWithNode (packages/shared/src/shell.ts:95-133) loops over every PATH entry times every candidate extension and calls NodeFS.statSync for each. Nothing is memoized, and the work is synchronous on the event loop.
  • On this machine that is ~22 ms per spawn. 2345 shell.resolveSpawnCommand spans in a 76-minute trace; ~580 s over 13 h.
  • Git for Windows puts Git\cmd on PATH ahead of Git\mingw64\bin, so the first hit is cmd\git.EXE, a launcher that execs the real mingw64\bin\git.exe. Every git command is two process creations (plus a conhost.exe each, see [Bug]: Frequent cmd.exe / conhost flashes on Windows from provider probe & VCS process kill paths #2537).

Steps to reproduce

  1. Windows with Git for Windows installed the default way.
  2. Run the desktop app or t3 serve with a git project and let the background sweeps run (see Thread PR discovery and settlement sweeps run every minute without client demand, and their caches expire before the next sweep #11220).
  3. Compare shell.resolveSpawnCommand span durations in server.trace.ndjson, and trace process creation: each runGitCommand shows git.exe (launcher) → git.exe (mingw64).

Expected: resolve a command once per (command, PATH, PATHEXT) and reuse it; resolve git to the real binary.
Actual: a full synchronous PATH scan per spawn, and a two-process git.

Version

0.0.41-nightly.20260908.1414 (desktop); code unchanged on main @ 211618f.

Environment

Windows 11 Pro 26200, git 2.55.0.windows.5, gh 2.100.0.

Evidence

shell.resolveSpawnCommand    2345 spans / 76 min   (~22 ms each on this machine)
processRunner.runProcessCore 2345

Related issues

#2537 (per-process console cost), #7570 (stale shim resolution, closed). Neither covers the missing memoization or the launcher choice.

Fix applied or workaround

None. PATH order could be changed by hand, but the resolver would still scan on every spawn.

Filed by

Claude Code (Claude Fable 5.1), following the t3 triage playbook from a checkout of main.

Activity

  1. SkiTee3000 commented on Sep 18, 2026

    @SkiTee3000
    ContributorAuthor

    Kernel-level numbers from a 20 s ETW file-I/O trace on 0.0.43-nightly.20260917.1851 (code unchanged on main @ 4d36142):

    • The server process alone issued 90,830 file opens in 20 s (~4,500/s), more than any other process on the machine. Almost all are PATH × PATHEXT probes for git; 344 resolutions, roughly 250 opens each.
    • The launcher doubling is visible in process starts: 236 git spawned by the server, 511 git.exe starts counted in a 30 s profile.
    • Defender follows every probe: 24,482 file opens in the same 20 s, 7.7% of all busy CPU time.

    How this adds up to a machine-wide stutter is in #12498.

    Filed by Claude Code (Claude Fable 5.1), following the t3 triage playbook.

  2. juliusmarminge commented on Oct 10, 2026

    @juliusmarminge
    Member

    Note

    Grok responding on behalf of Julius.

    Both halves of this are now on main: #12600 scans PATH once per command instead of on every spawn, and #17707 runs Git for Windows' real git.exe instead of the cmd\git.exe launcher. Closing as fixed. If you still see the two-process git or repeated PATH scans on a build that includes both, please reopen with a fresh trace.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions