fix(windows): resolve the captured PATH case-insensitively (fixes #232) - #248
Merged
yaojin3616 merged 1 commit intoAug 31, 2026
Conversation
…nt#232) Windows stores environment variable names with whatever casing the registry value name carries, and `resolveShellEnvironment()` returns a plain object keyed by that casing verbatim. A machine whose PATH value name is stored lowercase hands the launch path the key `path`; the exact-case read at the spawn-options site then produced an empty PATH, so the Harness started with no PATH at all and every PATH-resolved tool call failed with ENOENT. `process.env` hides this because Node makes it case-insensitive on win32, but that behaviour does not survive a spread: a copy keeps only the stored casing. `buildProfilePluginCommandEnvironment` spreads its environment before reading `Path`/`PATH` exact-case, so plugin install, removal, and repair subprocesses dropped the user's PATH on the same machines — only the shim and bundled-node directories survived. Add `resolveEnvironmentPath()`: exact-case `Path`/`PATH` reads first, then a case-insensitive scan, win32 only — POSIX keeps the exact read because `path` is a genuinely different variable there. Use it at both read sites.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes #232. Also unblocks #234, which is a downstream symptom of the same empty PATH.
Root cause
Windows stores environment variable names with whatever casing the registry value name carries, and
resolveShellEnvironment()returns a plain object keyed by that casing verbatim (parseEnvOutput,src/main/runtime/harness-runtime.ts). On a machine whose PATH value name is stored lowercase, the captured block arrives aspath— and the exact-case read at the spawn-options site:misses it, so the Harness spawns with
Path="". The stray lowercasepathcarried along by the spread does not save it: Node's win32 spawn sorts env keys and case-insensitively dedupes keeping the first, and'Path'sorts before'path', so the empty explicit key wins. Measured end-to-end on Windows 11 (26200) / node v22: a child spawned through the current code with a lowercase-pathblock seesprocess.env.PATH === "". Every PATH-resolved tool call then fails with ENOENT — includingpowershell.exefor "Open configuration file" (#234).process.envnever shows this problem because Node makes it case-insensitive on win32 — but that behaviour does not survive a spread.({...process.env}).Pathisundefinedon a lowercase-pathmachine even thoughprocess.env.Pathis defined.buildProfilePluginCommandEnvironment(src/main/runtime/profile-plugin-command.ts) spreads its environment before readingPath/PATHexact-case, so plugin install/removal/repair subprocesses dropped the user's real PATH on the same machines: only the shim and bundled-node directories survived.Fix
New helper
resolveEnvironmentPath(environment, platform)inharness-runtime.ts:PaththenPATHreads first (preserves existing behaviour for every input the current tests cover),pathis a genuinely different variable there,used at both read sites. The write sites are untouched: writing
Path/PATHnext to the original lowercase key is safe because whichever key survives Node's win32 dedupe carries the same resolved value.Verification
npm test— full suite passes on Windows: 74 files, 556 tests.npm run typecheck— clean.pathblock; before the fix the child receivesPATH="", after the fix it receives the full user PATH. (The lowercase value name itself comes from the reporter's PEB measurement in [Bug] Windows: Harness spawned with empty PATH when env block stores PATH as lowercase 'path' — user tools invisible to tool shells #232; I did not modify this machine's registry to reproduce that part.)New regression tests:
test/runtime.test.ts—buildHarnessSpawnOptionswith a lowercasepathblock: every key spelling PATH in the returned env must carry the user's value.test/shell-environment-encoding.test.ts— helper-level casing matrix, including the POSIX exact-case guarantee.test/profile-plugin-command.test.ts—buildProfilePluginCommandEnvironmentkeeps the user PATH from a lowercase block on win32, and keepspathout of PATH on POSIX.