Skip to content

fix(lsp/process): prefer PATHEXT extensions over extensionless name (#4262)#5

Open
YOMXXX wants to merge 1 commit into
code-yeongyu:mainfrom
YOMXXX:fix/windows-pathext-wrapper-script
Open

fix(lsp/process): prefer PATHEXT extensions over extensionless name (#4262)#5
YOMXXX wants to merge 1 commit into
code-yeongyu:mainfrom
YOMXXX:fix/windows-pathext-wrapper-script

Conversation

@YOMXXX

@YOMXXX YOMXXX commented May 22, 2026

Copy link
Copy Markdown

Closes code-yeongyu/oh-my-openagent#4262.

Root cause

getWindowsPathExtensions returned the candidate extension list with an empty string at the front:

```ts
return [...new Set(["", ...extensions, ".exe", ".cmd", ".bat"])];
```

On Windows, when an LSP server ships both an extensionless shebang script and a `.bat`/`.cmd` companion — `jdtls` is the canonical case — `resolveWindowsCommand`'s inner loop tried the empty string first, matched the extensionless file via `existsSync`, and returned its full path.

`isWindowsShellShim` then returned `false` (the resolved path doesn't end with `.bat`/`.cmd`), so Node spawned the extensionless Python script directly. Windows can't honor shebangs, so spawn failed with `UV_ENOENT` (`-4058`) and the language server never came up.

Fix

Move the empty string to the end of the array so PATHEXT extensions get checked first — matching `cmd.exe`'s actual lookup order:

```ts
return [...new Set([...extensions, ".exe", ".cmd", ".bat", ""])];
```

Now `jdtls.bat` wins, `isWindowsShellShim` returns true, and the wrapper goes through `cmd.exe /d /s /c jdtls.bat ...` — which is what jdtls needs to set `JAVA_HOME` correctly.

Test plan

  • New regression in `test/process.test.ts`: drops both files (`jdtls` extensionless shebang + `jdtls.bat` wrapper) into a tmp dir and asserts `createSpawnCommand(["jdtls", ...], "win32", ...)` returns `cmd.exe /d /s /c /jdtls.bat`.
  • RED-GREEN verified locally: revert the array reordering and the new test fails with the extensionless script selected; restore and 28/28 tests pass.

Summary by cubic

Prefer Windows PATHEXT extensions over extensionless names when resolving LSP binaries. Ensures jdtls and similar servers run via their .bat/.cmd wrapper instead of the shebang script, avoiding UV_ENOENT.

  • Bug Fixes
    • Reordered getWindowsPathExtensions to try PATHEXT first and the empty string last, matching cmd.exe lookup; prevents spawning extensionless scripts.
    • Added a regression test asserting createSpawnCommand(["jdtls", "--stdio"], "win32", ...) resolves to cmd.exe /d /s /c jdtls.bat.

Written for commit 4023034. Summary will update on new commits. Review in cubic

…n Windows (#4262)

`getWindowsPathExtensions` returned `["", ...extensions, ".exe", ".cmd", ".bat"]`
— with the empty string first. On Windows, when a wrapper-script-style LSP server
ships both an extensionless shebang script and a `.bat`/`.cmd` companion (jdtls
is the canonical case), `resolveWindowsCommand` matched the extensionless file
first because it tried `""` before `.BAT`/`.CMD`. `isWindowsShellShim` then
returned false (the resolved path doesn't end in `.bat`/`.cmd`), so Node spawned
the extensionless Python script directly. Windows ignores shebangs, so the spawn
failed with UV_ENOENT (-4058) and the LSP server never came up.

Move the empty string to the end of the array so PATHEXT extensions get checked
first — matching cmd.exe's lookup order. The wrapper wins, isWindowsShellShim
returns true, and node spawns through `cmd.exe /d /s /c jdtls.bat ...`.

Includes a regression test that drops both files (`jdtls` + `jdtls.bat`) into
a tmp dir and asserts the resolver picks the wrapper.
@YOMXXX YOMXXX requested a review from code-yeongyu as a code owner May 22, 2026 06:49

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

No issues found across 2 files

Re-trigger cubic

@YOMXXX

YOMXXX commented May 22, 2026

Copy link
Copy Markdown
Author

Cross-reference: this PR is part of a roadmap covering 12 PRs across both oh-my-openagent and this submodule. See the rollup at code-yeongyu/oh-my-openagent#4293 — it groups bug fixes, regression locks, and new features with a suggested review order.

Direct link: code-yeongyu/oh-my-openagent#4293

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug]: LSP server fails to start on Windows when wrapper script (.bat/.cmd) is needed

1 participant