Skip to content

Every script run without a terminal starts a second sh since 12.7.0 and 11.28.0 #16489

Closed

Description

@altaywtf

Verify latest release

  • I verified that the issue exists in the latest pnpm release

pnpm version

12.8.2 and 11.28.3. Last good: 12.6.0 and 11.27.1. First bad: 12.7.0 and 11.28.0.

Which area(s) of pnpm are affected? (leave empty if unsure)

CLI

Link to the code that reproduces this issue or a replay of the bug

No response

Reproduction steps

Run pnpm without a controlling terminal, as CI, IDE task runners and agents do:

mkdir ws && cd ws
echo '{"name":"root","private":true}' > package.json
printf 'packages:\n  - "packages/*"\n' > pnpm-workspace.yaml
# Records the watchdog shells whose parent is the pnpm running the script ($1).
cat > spy.sh <<'SPY'
ps -A -o pid= -o ppid= -o args= | awk -v pnpm="$1" '$2 == pnpm && /INT TERM HUP/ { print $1 }' >> "$(dirname "$0")/watchdogs.log"
SPY
for i in $(seq 1 50); do
  mkdir -p packages/p$i
  echo "{\"name\":\"p$i\",\"version\":\"1.0.0\",\"scripts\":{\"noop\":\"true\",\"spy\":\"sh ../../spy.sh \$PPID\"}}" > packages/p$i/package.json
done
pnpm -r run spy </dev/null >/dev/null 2>&1
sort -u watchdogs.log | wc -l   # watchdog shells started for 50 scripts
/usr/bin/time -l pnpm -r run noop </dev/null >/dev/null 2>&1   # macOS: "instructions retired"

Results on macOS arm64 (instructions retired by the pnpm process, median of 5 runs, 50-entry PATH):

pnpm watchdog shells for 50 scripts instructions
12.6.0 0 547M
12.7.0 50 873M
12.8.2 50 793M
11.27.1 0 3.91G
11.28.3 50 5.31G

Describe the Bug

Since #15564, pnpm starts a watchdog sh (trap '' INT TERM HUP; read -r _ || kill -9 -$1) next to every script that runs in a process group of its own. A script gets its own group whenever pnpm has no controlling terminal. In pnpm 12, every script of a concurrent recursive run gets one too. So pnpm -r run over N projects starts 2N shells, and every pnpm run, pnpm exec and lifecycle script in CI pays an extra process spawn.

On macOS the cost grows with PATH length, because the bare sh is looked up on PATH for every spawn. With a 96-entry PATH, pnpm -r run noop over 50 projects costs 1.17G instructions on current main, against 0.59G with one watchdog.

The guarantee from #15564 (a script's process group dies when pnpm is killed) does not need a shell per script.

Expected Behavior

One process per script, as in 12.6.0 and 11.27.1, while a script's process group still ends when pnpm is killed.

Which Node.js version are you using?

24.21.0

Which operating systems have you used?

  • macOS
  • Windows
  • Linux

If your OS is a Linux based, which one it is? (Include the version if relevant)

Measured on macOS only. The same code path runs on Linux; Windows uses a Job Object and has no watchdog.

Contributing a fix

  • I am working on a fix

Written by an agent (Claude Code, Opus 5.5).

Activity

  1. altaywtf commented on Oct 1, 2026

    @altaywtf
    MemberAuthor

    I'm working on a fix for this.


    Written by an agent (Claude Code, Opus 5.5).

  2. altaywtf commented on Oct 1, 2026

    @altaywtf
    MemberAuthor

    A fix is ready for review in #16490. CI is green.

    Written by an agent (Claude Code, Opus 5.5).

  3. self-assigned this
    on Oct 1, 2026
  4. added a commit that references this issue on Oct 1, 2026
    88bf5e4
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions