DeepSeek Harness Windows spawn 4s slower: pwsh wrapper

TroubleshootingPublished 2026-10-30Author: DeepSeek Plugin Market
DeepSeek HarnessDSHWindowsperformancepwshhooksspawn
Each spawn in DeepSeek Harness on Windows costs 4-6 seconds, even read. Four timing segments separate tools, hooks, the pwsh wrapper, and process creation.

echo 1 takes milliseconds in a terminal but 10 seconds end to end in DSH: 4.4 seconds are the hook, and another 6 run from hook end to the tool result. Once that baseline exists, the problem is pinned to "every spawn pays a fixed cost", not the model or the network (#10608). Four timing segments narrow it down step by step.

DeepSeek Harness: Setup: use echo 1 as the baseline

Pick the command least likely to be slow, measure its end-to-end time, then break down where the time goes.

  1. In a session, have the agent run echo 1 and record the wall time from tool/call to tool/result.
  2. Community result: about 10 seconds, of which 4.4 were the PreToolUse hook and about 6 ran from hook end to result.
  3. Run the same command in a terminal and it returns almost instantly.
  4. Conclusion: the call chain is slow, not the command content.

DSH plugin: Segment 1: time tools that do not spawn

read and grep start no subprocess; if they are also slow, the overhead is around the call rather than inside the tool.

  1. Aggregate tool durations over a window: in the community sample read ran 49 times averaging 4,860 ms, and grep 11 times averaging 4,309 ms.
  2. Compare with the hook average: PreToolUse ran 324 times averaging 4,046 ms.
  3. The two almost coincide, so read/grep time is essentially hook time.
  4. pwsh averaged 10,621 ms, and the extra is the pwsh process starting itself.
  5. mcp__…save averaged 4,251 ms; MCP subprocesses pay the same fixed cost.
ToolSpawns?Average
readNo4,860 ms
grepNo4,309 ms
pwshYes10,621 ms
mcp__…saveYes4,251 ms

This segment rules out "a slow tool implementation": non-spawning tools are just as slow.

DeepSeek Harness: Segment 2: isolate the hook and measure it alone

Make the hook script exit on its first line; if the duration does not change, the cost is not in the script.

  1. Measure the four hooks in hooks.json: SessionStart, UserPromptSubmit, PreToolUse, PostToolUse.
  2. Record the hook/result.durationMs DSH reports: PreToolUse averages 4,046 ms, PostToolUse 4,024 ms, UserPromptSubmit 4,310 ms.
  3. Run the same scripts manually in a terminal: 206 ms, 51 ms, 51 ms.
  4. Run one more control: make block_delete.js start with process.exit(0) and do nothing.
  5. DSH still records 4,298-4,429 ms, with only 131 ms of variation across 8 consecutive calls.
  6. Conclusion: this is a fixed cost unrelated to script workload.

DSH Plugin: Segment 3: measure shell startup itself

If a shell starts in 0.2 seconds but the call takes 4, the extra time lives in the wrapper and process creation outside the shell.

  1. Time them in a terminal: pwsh -NoProfile -Command exit takes about 190 ms, pwsh -Command exit about 198 ms.
  2. powershell.exe 5.1 takes about 132-149 ms, cmd /c exit about 31 ms, node -e 0 about 38-48 ms.
  3. -NoProfile saves only 8 ms, so the "profile or module autoload is slow" hypothesis is out.
  4. In other words, no single shell startup comes anywhere near 4 seconds.
  5. The cost equals that startup plus everything the runner does around it, paid every time.

DeepSeek Harness: Segment 4: inspect the process tree for the middle layer

Open the command line column in Task Manager or a process viewer and see who the parent of node.exe is — this step exposes the wrapper directly.

  1. Trigger one hook call and watch the process tree at the same time.
  2. Community finding: node.exe is not a direct child of DSH but a grandchild under the pwsh wrapper.
  3. Match it with the source shape: DSH wraps commands uniformly as pwsh -NoLogo -NoProfile -NonInteractive -Command "<command>".
  4. For commands shaped as "absolute path + arguments", the wrapper is pure overhead.
  5. The four segments now close: the tool is fine, the hook script is fine, shell startup is fine, and the problem is the wrapper.

DSH plugin: Fix: call CreateProcessW when the command shape is simple

The community patch makes the executor's argv() attempt a simple split first, calling CreateProcessW directly when possible and falling back to the original path otherwise.

  1. Only commands shaped as "absolute path + arguments" take the direct path.
  2. Anything containing pipes, redirection, $, backticks, #, newlines, smart quotes, unbalanced quotes, a relative first token, .cmd/.bat/.ps1, or --% falls back.
  3. Timeout, buffering, and exit code handling are untouched.
  4. Measured on the same machine and version after the patch: PreToolUse dropped from 4,046 ms to 240-255 ms; read to 144-177 ms; grep to 362-395 ms; a python MCP call to 151 ms.
  5. Note that 240-255 ms covers only spawn plus script execution and excludes about 200 ms waiting for the output stream to close, so the whole hook is about 440 ms.
  6. About 1.3 seconds of waiting still sits in NtCreateUserProcess, below the JS layer, and this patch cannot reach it.

Version status and what you can do

The wrapper is still present on rc.3, so commands using shell syntax still take 4.2-4.7 seconds; all you can do is reduce how often it triggers.

  1. On 0.2.1-rc.3, argv() still unconditionally returns the pwsh wrapper; the community measured 0.4-0.6 seconds for the direct-spawn path and 4.2-4.7 seconds for shell commands.
  2. A python.exe -c "…" with quoted arguments is judged back to the shell by the scanner and still measures 4,506 ms.
  3. What you can do: reduce the number of hooks and how often they fire, especially PreToolUse.
  4. Write hook commands as "absolute path + arguments" where possible, avoiding pipes and redirection.
  5. Do not go add Defender exclusions — the community neither tried nor needed it, and the real cause is the wrapper.
  6. A complete fix needs upstream to route hooks through ctx.subprocess, or to skip the shell when the command is already argv.

Cautions

Time each layer before blaming the model or the machine.

  1. Do not mistake a fixed delay for network jitter. Eight calls varied by only 131 ms; this is a fixed cost.
  2. Do not suspect the profile first. -NoProfile saves only 8 ms.
  3. Do not look only at hooks. The pwsh tool also stacks its own startup on top.
  4. The patch is not a release build. When reporting, state the version and whether the patch is applied.
  5. Record the process tree. It is the fastest picture separating "slow tool" from "slow wrapper".

If your machine shows "a few seconds occasionally" rather than "a few seconds every time", it is more likely sandbox or ACL related — change direction.See DeepSeek Harness slow startup: causes and fixes for dsh for the related case.

FAQ

Every command on Windows is 4 seconds slower in DeepSeek Harness — is the model slow or the tool?

The slowdown comes from the DeepSeek Harness tool layer, not the model. The community used echo 1 as a baseline: 10 seconds end to end, of which about 4.4 were the PreToolUse hook and another 6 ran from hook end to tool result. echo 1 itself takes milliseconds.

Why are read and grep slow in DeepSeek Harness even though they do not start a subprocess?

read and grep are slow in DeepSeek Harness because they pass through a PreToolUse hook on every call, and the hook itself spawns a process. The community measured read at 4,860 ms and grep at 4,309 ms on average, almost identical to the hook's 4,046 ms.

How do I prove the slowness in DeepSeek Harness is not the hook script itself?

To prove the slowness is not the hook script itself in DeepSeek Harness, make the script exit on its first line with process.exit(0), doing nothing, and the duration DSH records is still 4,298-4,429 ms; the same script run manually in a terminal takes only 51-206 ms. The gap is in the wrapper layer, not the script.

What is the middle layer the community found in DeepSeek Harness?

The middle layer is the pwsh wrapper in DeepSeek Harness. DSH wraps every command as pwsh -NoLogo -NoProfile -NonInteractive -Command "<command>", and node.exe is actually a grandchild of pwsh. Removing that wrapper drops hooks to 240-255 ms.

Did the official DeepSeek Harness build fix it?

No, the official DeepSeek Harness build was not verified to contain the fix, because the community patch was only tested on their own build. On 0.2.1-rc.3, argv() still unconditionally returns the pwsh wrapper, commands using shell syntax still take 4.2-4.7 seconds, while the direct-spawn path is 0.4-0.6 seconds.

Related Terms

PreToolUse hook
A PreToolUse hook is a Claude Code-style hook that runs before every tool call. DSH runs it through a shell, so it introduces additional process startup overhead.— https://github.com/deepseek-ai/deepseek-harness/discussions/10608
pwsh wrapper
The pwsh wrapper is how DSH on Windows packages commands uniformly as pwsh -Command before handing them to the executor. For commands already shaped as an absolute path plus arguments, this layer is pure overhead.— https://github.com/deepseek-ai/deepseek-harness/discussions/10608
CreateProcessW
CreateProcessW is the low-level Windows process creation API. The community patch calls it directly when the command shape is simple, bypassing the shell, and otherwise falls back to the original wrapper path.— https://github.com/deepseek-ai/deepseek-harness/discussions/10608
NtCreateUserProcess
NtCreateUserProcess is the system call CreateProcessW uses internally to create a process. The community observed about 1.3 seconds of remaining wait parked there, below the JS layer.— https://github.com/deepseek-ai/deepseek-harness/discussions/10608

Sources