-
Notifications
You must be signed in to change notification settings - Fork 84
Description
On Windows, RTK fails to spawn JS/TS tools that are installed as .CMD/.BAT/.PS1 shims (e.g. via pnpm, npm). This affects all Node.js-based tools: vitest, eslint, tsc, prettier, pnpm, npm, npx, playwright, stylelint, prisma, next, etc.
Problem
Any RTK subcommand that internally spawns a JS/TS tool fails with "program not found":
$ rtk vitest run
Error: Failed to run vitest
Caused by:
program not found
$ rtk lint src/
Error: Failed to run eslint. Is it installed?
Caused by:
program not found
$ rtk tsc --noEmit
Error: Failed to run tsc (try: npm install -g typescript)
Caused by:
program not found
$ rtk pnpm run build
Error: Failed to run pnpm
Caused by:
program not foundEven rtk proxy shows the same behavior:
$ rtk proxy vitest --version
Error: Failed to execute command: vitest
Caused by:
program not foundRoot cause
On Windows, Node.js tools are not native .exe binaries. They are installed as script wrappers:
C:\Users\artio\AppData\Local\pnpm\vitest # #!/bin/sh shell script
C:\Users\artio\AppData\Local\pnpm\vitest.CMD # Windows batch wrapper
C:\Users\artio\AppData\Local\pnpm\vitest.ps1 # PowerShell wrapper
Windows shells (cmd.exe, PowerShell, Git Bash) resolve these automatically using the PATHEXT environment variable (.COM;.EXE;.BAT;.CMD;.VBS;.VBE;.JS;.JSE;.WSF;.WSH;.MSC;.CPL). However, Rust's std::process::Command::new("vitest") does not honor PATHEXT — it only finds exact filename matches or .exe files.
Proof that the tools are on PATH and work outside RTK:
$ where vitest
C:\Users\artio\AppData\Local\pnpm\vitest
C:\Users\artio\AppData\Local\pnpm\vitest.CMD
$ vitest --version
vitest/4.0.18 win32-x64 node-v24.13.1
$ rtk proxy "C:/Users/artio/AppData/Local/pnpm/vitest.CMD" --version
vitest/4.0.18 win32-x64 node-v24.13.1 # works with full .CMD path!Expected behavior
RTK should resolve binaries using PATHEXT on Windows, the same way cmd.exe and PowerShell do. When spawning vitest, it should find vitest.CMD (or .BAT, .PS1) on PATH.
Workaround
Using rtk err cmd /c <tool> partially works because cmd.exe IS a native .exe and handles PATHEXT resolution:
$ rtk err cmd /c vitest run --run
✅ Command completed successfully (no errors)
$ rtk err cmd /c eslint src/
✅ Command completed successfully (no errors)This loses the specialized filtering (90% token reduction) from rtk vitest/rtk lint/rtk tsc but at least provides error-only filtering.
rtk err does not propagate the exit code from the wrapped command. It always reports ✅ Command completed successfully (no errors) even when the underlying tool exits with a non-zero code (e.g. eslint --max-warnings 0 finding violations). This makes rtk err cmd /c unreliable for CI-style checks where the exit code determines pass/fail. For tools where exit codes matter (linters, type-checkers, test runners), running them directly without rtk err wrapping is recommended until this is fixed.
Suggested fix
When resolving binary paths on Windows, iterate through PATHEXT extensions. The which crate for Rust handles this correctly out of the box. This is analogous to what cross-spawn does in the Node.js ecosystem.
Environment
- RTK: v0.22.0
- OS: Windows 11 (10.0.26200)
- Shell: Git Bash (MSYS2) + PowerShell 7
- Node: v24.13.1
- pnpm: v10.30.0