title: "[Windows] dsh1024 plugin command silently fails (exit 1) when dsh.cmd is on PATH — spawn EINVAL"
labels: bug, windows
Describe the bug
On Windows, dsh1024 plugin --profile <profile> add <pkg> exits with code 1 and no output at all whenever an official dsh CLI shim (a dsh.cmd batch file) is present on PATH. The wrapper picks the .cmd shim from PATH and spawns it with shell: false, which Node.js cannot do on Windows (.bat/.cmd files require a shell) — the spawn throws EINVAL, the error is swallowed, and the CLI exits 1 silently. Running the same command through the official dsh CLI directly works fine.
Environment
- OS: Windows (win32, x64)
- Node: v24.13.1
- npm: 11.8.0
- pnpm: 10.30.2
- dsh1024: 0.5.0 (latest)
To reproduce
-
Have an official @deepseek-ai/dsh shim on PATH, e.g. ...\node_modules\.bin\dsh.cmd (from a global install or an npx cache).
-
Run:
npm install -g dsh1024
dsh1024 plugin --profile web add dsh1024@latest
-
Observe: the command prints nothing and exits with code 1.
-
Control: dsh plugin --profile web add dsh1024@latest succeeds (pnpm output shown, exit 0).
Expected behavior
The command forwards to the official CLI and reports the install outcome, exiting 0 on success.
Actual behavior
Silent exit code 1. With NODE_DEBUG=child_process the wrapper is seen spawning:
file: 'C:\\Program Files\\nodejs\\node_cache\\_npx\\...\\node_modules\\.bin\\dsh.cmd',
args: [ 'plugin', '--profile', 'web', 'add', 'dsh1024@latest' ],
shell: false,
which throws Error: spawn EINVAL (errno -4071) synchronously from spawn(). runOfficialCommand resolves with exitCode: null and the error is never printed (cli/plugin.js only maps it to failureCode === 'SPAWN_FAILED'), so the user sees nothing.
Root cause
cli/plugin.js → findOfficialCliOnPath() scans PATH using PATHEXT extensions (.COM;.EXE;.BAT;.CMD) and returns the first hit, which on typical Windows setups is a dsh.cmd npm shim.
officialCliInvocation() then returns { file: '<...>\\dsh.cmd', prefixArgs: [], useShell: false }.
lib/shared/install-runner.js → runOfficialCommand() spawns it with shell: false. On Windows, .bat/.cmd files can only be executed through a shell (cmd.exe), so Node throws EINVAL from spawn() (see Node docs: "batch files can only be launched with the shell option").
- The wrapper treats this as a failed install and exits 1, printing nothing.
Suggested fix
In the win32 branch of officialCliInvocation() (or in findOfficialCliOnPath()):
- Prefer a real executable (
dsh.exe); if the only match is a .cmd/.bat shim, either
- spawn it with
useShell: true, or
- skip
.cmd/.bat in the PATH scan and fall back to the npm exec route (node <npm-cli.js> exec --yes -- @deepseek-ai/dsh ...), which already works on Windows.
Also consider printing result.error to stderr when failureCode === 'SPAWN_FAILED' so failures are never silent.
Workaround (verified)
$env:DSH1024_DSH_PACKAGE='@deepseek-ai/dsh'
dsh1024 plugin --profile web add dsh1024@latest
Setting DSH1024_DSH_PACKAGE forces the wrapper to skip the PATH lookup and use the npm exec route, which succeeds (exit 0, receipts recorded correctly in ~/.dsh/.dsh-1024store/receipts.json).
title: "[Windows] dsh1024 plugin command silently fails (exit 1) when dsh.cmd is on PATH — spawn EINVAL"
labels: bug, windows
Describe the bug
On Windows,
dsh1024 plugin --profile <profile> add <pkg>exits with code 1 and no output at all whenever an officialdshCLI shim (adsh.cmdbatch file) is present onPATH. The wrapper picks the.cmdshim from PATH and spawns it withshell: false, which Node.js cannot do on Windows (.bat/.cmdfiles require a shell) — the spawn throwsEINVAL, the error is swallowed, and the CLI exits 1 silently. Running the same command through the officialdshCLI directly works fine.Environment
To reproduce
Have an official
@deepseek-ai/dshshim on PATH, e.g....\node_modules\.bin\dsh.cmd(from a global install or an npx cache).Run:
Observe: the command prints nothing and exits with code 1.
Control:
dsh plugin --profile web add dsh1024@latestsucceeds (pnpm output shown, exit 0).Expected behavior
The command forwards to the official CLI and reports the install outcome, exiting 0 on success.
Actual behavior
Silent exit code 1. With
NODE_DEBUG=child_processthe wrapper is seen spawning:which throws
Error: spawn EINVAL(errno -4071) synchronously fromspawn().runOfficialCommandresolves withexitCode: nulland the error is never printed (cli/plugin.jsonly maps it tofailureCode === 'SPAWN_FAILED'), so the user sees nothing.Root cause
cli/plugin.js→findOfficialCliOnPath()scans PATH usingPATHEXTextensions (.COM;.EXE;.BAT;.CMD) and returns the first hit, which on typical Windows setups is adsh.cmdnpm shim.officialCliInvocation()then returns{ file: '<...>\\dsh.cmd', prefixArgs: [], useShell: false }.lib/shared/install-runner.js→runOfficialCommand()spawns it withshell: false. On Windows,.bat/.cmdfiles can only be executed through a shell (cmd.exe), so Node throwsEINVALfromspawn()(see Node docs: "batch files can only be launched with the shell option").Suggested fix
In the win32 branch of
officialCliInvocation()(or infindOfficialCliOnPath()):dsh.exe); if the only match is a.cmd/.batshim, eitheruseShell: true, or.cmd/.batin the PATH scan and fall back to thenpm execroute (node <npm-cli.js> exec --yes -- @deepseek-ai/dsh ...), which already works on Windows.Also consider printing
result.errorto stderr whenfailureCode === 'SPAWN_FAILED'so failures are never silent.Workaround (verified)
Setting
DSH1024_DSH_PACKAGEforces the wrapper to skip the PATH lookup and use thenpm execroute, which succeeds (exit 0, receipts recorded correctly in~/.dsh/.dsh-1024store/receipts.json).