ctx.subprocess.spawn() hangs on Windows when the spawned process has its own nested subprocess children (ctx.shell.run works) #4796
Replies: 1 comment
|
I would not treat The useful first split is:
The rc.2 local subprocess implementation already bounds case 2: after direct-child For a disposable reproduction, I would capture this matrix with identical argv/cwd/env/input/deadline:
A sufficient upstream Windows test should prove direct parent + multiple grandchildren, collected output preservation, I wrote up the source references, decision table, evidence matrix, and acceptance gates here: https://github.com/sandbaseai/deepseek-harness-handbook/blob/main/docs/en/plugin-development/async-subprocess-tools.md#route-a-windows-nested-child-hang-before-changing-executors |
Uh oh!
There was an error while loading. Please reload this page.
Summary
ctx.subprocess.spawn()(from@deepseek-ai/dsh-subprocess-local) hangs indefinitely — until whatevergraceMs/outer timeout kills it — when the spawned process itself spawns further nested subprocesses (grandchildren), on Windows. The identical command run throughctx.shell.run()(the service backingdsh-tool-pwsh/dsh-tool-bash, which also ultimately callsctx.subprocess.spawn()internally) completes normally.Repro
Environment:
@deepseek-ai/dsh0.1.1-rc.2, Windows 11, Nodev24.16.0.A profile plugin (Cordis composition entry,
inject: ['subprocess']) that does:— where
scripts/preflight.pyitself runs ~19 further subprocess calls (each a short-livedpython some_gate.pyinvocation via Python's ownsubprocess.run) — never completes. It sits until the caller's own outer deadline fires and force-terminates the tree; the direct child (python.exe) shows no forward progress in that time (confirmed viaGet-Process -Id <pid> | Select CPUsampled repeatedly: CPU time stops climbing).The exact same command, run through the agent's own
pwshtool (ctx.shell.resolve({command: 'python scripts\\preflight.py', workdir: repoRoot})→ctx.shell.run(...)), completes normally — 8.2s wall time — same repo, same working directory, same process tree shape (one parent, ~19 short-lived grandchildren).Tools with a single level of subprocess (the plugin's own direct child does no further spawning) do not exhibit this — they return normally via
ctx.subprocess.spawn()in the same plugin, same session.What differs between the two paths
Both
ctx.subprocess.spawn()(used directly) andctx.shell.run()(used bydsh-tool-pwsh, via@deepseek-ai/dsh-pwsh-local'sPwshLocalExecutor.runArgv) ultimately call the identicalspawnSubprocess()indsh-subprocess-local/lib/index.js— plainchild_process.spawn(program, args, {cwd, env, stdio: [...], detached: platform !== "win32"}), no shell, stdout/stderr as anonymous pipes drained viastream.on('data', ...).The difference I can identify without access to the private path is the invocation shape:
argv: [pythonExe, 'scripts/preflight.py']— no intermediate shell.argv: [pwshPath, '-NoLogo', '-NoProfile', '-NonInteractive', '-Command', '<preamble><command>']— pwsh interpretsspec.commandas script text, which resolves to the samepython scripts\preflight.pyinvocation, but with pwsh itself as the direct child instead of python.I could not find a second variable that differs; env construction, stdio disposition, and stdin (
'ignore'in both) are the same in both call sites' resolved spec. Wrapping the direct call incmd.exe /d /s /c "python scripts\preflight.py"(still no pwsh) did not fix the hang — so it is not simply "no shell at all" vs "any shell"; something specific to how the eventualpython.exeprocess is parented differs between "direct child of the Node harness process" and "child of a pwsh process that is itself a child of the harness process."Impact
Any profile-level plugin that wraps a script performing further subprocess calls of its own (a orchestrator/runner pattern — this is common for gate/lint/test-runner scripts) via
ctx.subprocess.spawn()directly is affected on Windows. The workaround is to route such calls throughctx.shell.run()instead (inject'shell'), which is what I did — but that service is intended fordsh-tool-pwsh/dsh-tool-bash's own use, so this may not be the intended public surface for it, and I'm not confident it's meant to be a general substitute forctx.subprocess.spawn().Environment
@deepseek-ai/dsh0.1.1-rc.2,@deepseek-ai/dsh-subprocess-local(bundled),@deepseek-ai/dsh-pwsh-local(bundled)v24.16.0, Windows 11ctx.shell) always completes.All reactions