Repository navigation
[Bug]: Windows DEP0190 from resolveSpawnCommand spawn(args, { shell: true }) for .cmd/.bat shims #12797
Description
Activity
Triage
Confirmed on current
main. This is a real Windows bug in the shared spawn helper, not a duplicate of #2537.Diagnosis
packages/shared/src/shell.tsresolveSpawnCommandstill does this for.cmd/.bat:return { command: escapeWindowsShellArg(resolvedCommand), args: sanitizeShellModeArgsForPlatform(args, platform), shell: true, };
That is exactly Node’s DEP0190 path:
spawn(file, args, { shell: true })concatenates args and hands them to%ComSpec% /d /s /c …. Nightly embeds Node 24, so every such spawn prints:[DEP0190] DeprecationWarning: Passing args to a child process with shell option true can lead to security vulnerabilities, as the arguments are not escaped, only concatenated.Desktop logs that as
desktop-backend-childstderr (backend child process output). The captured linecmd.exe /d /s /c ""…\npm\opencode.CMD" "serve" "--hostname=127.0.0.1" "--port=…"matches OpenCode’s
startOpenCodeServerProcessgoing through this helper (apps/server/src/provider/opencodeRuntime.ts). Same helper is used for Codex/Claude/Cursor/Grok probes,processRunner, ACP, andnpm→npm.cmdprovider updates.T3 already escapes each arg (
escapeWindowsShellArgdocuments Node’s unquoted join). This is noisy / a visiblecmd.exeor Windows Terminal tab, not an active injection hole. Chats still work..exe/.comalready take theshell: falsebranch, which is why pointing OpenCodebinaryPathatopencode-ai\bin\opencode.exeworks around it for that one provider.I did not find
cp.spawnSync('npm.cmd', ['view', …], { shell: true })in current source. Latest-version lookup is HTTP (fetchNpmLatestVersion).npm.cmdstill goes through this helper on updates.Not a duplicate of #2537
#2537 is Effect
taskkill/ missing hide on kill, plus idlegit/gh/conhostchurn. This issue is T3’s ownshell: true+ args for npm shims. Effect already defaultswindowsHidewhen not detached (@effect/platform-node-shared4.0.0-rc.115). Bumping Effect or hidingtaskkillwill not remove DEP0190. Keep both open; they share a Windows console symptom only.Related, not the same: #10818 (closed dup of #2537), #12498 (idle churn / WMI), #12600 (PATH-scan cache on this helper — does not change
shell: true).Suggested fix
Keep the existing quoting. Stop passing
shell: truetogether with an args array. For.cmd/.bat, spawn ComSpec directly:return { command: process.env.ComSpec || "cmd.exe", args: ["/d", "/s", "/c", buildCmdExeCommandLine(resolvedCommand, args)], shell: false, };
Callers already pass
shell: spawnCommand.shell, so the helper is the whole fix. Tests that currently assertshell: true:packages/shared/src/shell.test.ts(escapes the executable and arguments for Windows command shims)apps/server/src/provider/providerMaintenanceRunner.test.ts(resolves npm to a .cmd shim and routes through the shell on win32)
Claude’s SDK path already unwraps
.cmd→ real.exe(ClaudeExecutable.ts) because the SDK cannot useshell: true. Same idea for other providers is optional; the ComSpec spawn is the general fix.Workaround
Set the provider
binaryPathto the real.exe(for OpenCode:%APPDATA%\npm\node_modules\opencode-ai\bin\opencode.exe). That does not fixnpm.cmdor other shims.Severity
Cosmetic / low. Repeats on every backend start and every
.cmdprovider spawn. Actionable locally.Leaving this open as an accepted bug. #2537 stays on the Effect / idle-churn track.
- addedacceptedfeature request acceptedfeature request acceptedbugSomething is broken or behaving incorrectly.Something is broken or behaving incorrectly.via-triageFiled through npx t3 triageFiled through npx t3 triage
on Sep 20, 2026 I'd like to take this — working on a fix.
Before submitting
Related: #2537 (cmd.exe flashes from OpenCode
shell: true+taskkill). This report is the Node 24 DEP0190 path in the shared spawn helper, which is still present on Nightly0.0.43-nightly.20260920.2005.Area
packages/contracts or packages/shared
Steps to reproduce
binaryPathofopencode(the npm.cmdshim under%APPDATA%\npm\opencode.cmd).%USERPROFILE%\.t3\userdata\logs\server-child.log.Expected behavior
No Node deprecation warning, and no stray
cmd.execonsole tab, when T3 launches provider CLIs.Windows
.cmd/.batshims should be started as a singlecmd.exe /d /s /c "<escaped command line>"withshell: falseandwindowsHide: true(or by pointing at the real.exe).Actual behavior
A
cmd.exewindow / Windows Terminal tab appears with:Desktop backend stderr uses the Electron argv0, so the helper line is
Use T3 Code (Nightly) --trace-deprecation .... The CLI binary printsUse t3 --trace-deprecation .... Same warning.Process list on this machine while Nightly was running:
That is Node's
spawn(file, args, { shell: true })expansion.Impact
Cosmetic issue
It does not break chats, but it repeats on every backend start and on every
.cmdprovider spawn. Node documents DEP0190 as the insecure concatenate-args path; T3 already escapes args itself, so the warning is noisy rather than an active injection hole.Version or commit
T3 Code (Nightly)
0.0.43-nightly.20260920.2005Environment
Windows 11, T3 Code Nightly desktop, Node v24.13.0. OpenCode enabled via
%APPDATA%\npm\opencode.cmd→opencode-ai\bin\opencode.exe. Windows Terminal is the default console host (HKCU\Console\%%StartupDelegationTerminal{E12CFF52-A866-4C77-9A90-F570A7AA2C6B}).Logs or stack traces
From
~/.t3/userdata/logs/server-child.log(every backend start):Source still in Nightly
server.asar/ currentpackages/shared/src/shell.tsresolveSpawnCommand:The comments next to
escapeWindowsShellArgalready describe Node joining args with spaces and handing them tocmd.exeunquoted — that is exactly DEP0190.There is also
cp.spawnSync('npm.cmd', ['view', ...], { shell: true })in the asar for version lookup.Workaround
Point provider
binaryPathat the real.exesoresolveSpawnCommandtakes theshell: falsebranch. For OpenCode:%APPDATA%\npm\node_modules\opencode-ai\bin\opencode.exeThat avoids the npm
.cmdshim for that one provider. It does not fixnpm.cmdor other shims.Suggested fix
Keep the existing
escapeWindowsShellArg/sanitizeShellModeArgsForPlatformquoting, but stop passingshell: truetogether with an args array.When the resolved path is
.cmd/.bat, spawn ComSpec directly:Same pattern OpenClaw used for DEP0190 on Windows
.cmdrunners..exe/.compaths can stayshell: falseas they do today.