Summary
rushx-client forwards SIGINT/SIGTERM as requestCancel. The daemon then aborts the GlobalCommandExecutionContext, whose terminateChild immediately calls SubprocessTerminator.killProcessTree, which is process.kill(-pid, 'SIGKILL'). The script never receives SIGINT or SIGTERM. Dev servers leave lock/pid files and ports behind, and test runners skip teardown. RushXCommand then reports the killed shell as Error: An unknown error occurred., and the client exits 1 instead of 130/143.
Repro steps
Take a rushx script that traps SIGINT/SIGTERM/SIGHUP, writes a marker file, and exits 40.
| action |
rushx-client (daemon) |
native rushx |
| SIGINT to the client |
exit 1, child SIGKILLed, trap not run, prints "Error: An unknown error occurred." |
terminal Ctrl+C: trap runs, exit 130 |
| SIGTERM to the client |
exit 1, trap not run, same message |
exit 143 |
To its credit, the daemon kills the whole tree, so no orphans are left behind.
Expected result: Emulate a terminal Ctrl+C. Send the same signal (SIGINT/SIGTERM) to the script's process group, wait a bounded grace period (for example 2-5 s, within the client's cancellation timeout), then SIGKILL. The client exits 128 + signo and does not print a bogus "unknown error".
Actual result: The script is killed immediately with SIGKILL, the client exits 1, and the message is misleading.
Details
Root cause (main @ 60007c9): libraries/rush-daemon/src/GlobalCommandExecutionContext.ts:343-355 (terminateChild) calls killProcessTree (SIGKILL) directly. CommandResultPolicy.ts:65-68 maps the result to exit code 1.
Suggested fix: add a graceful phase: process.kill(-child.pid, requestSignal ?? 'SIGINT'), then killProcessTree after a grace timeout. Carry the client's signal in requestCancel (for example an additive optional signal field). Suppress the "unknown error" message for aborted requests, and return 130/143 (related: #6060, for phased builds).
This was found during an automated performance/behavior analysis of rush-client/rushd on Linux and independently reproduced.
Standard questions
| Question |
Answer |
@microsoft/rush globally installed version? |
built from main @ 60007c9 (5.179.0) |
rushVersion from rush.json? |
5.179.0 |
pnpmVersion, npmVersion, or yarnVersion from rush.json? |
pnpm@10.27.0 |
(if pnpm) useWorkspaces from pnpm-config.json? |
true |
| Operating system? |
Linux (WSL2 Ubuntu 24.04) |
| Would you consider contributing a PR? |
Yes |
Node.js version (node -v)? |
22.23.2 |
Summary
rushx-clientforwards SIGINT/SIGTERM asrequestCancel. The daemon then aborts theGlobalCommandExecutionContext, whoseterminateChildimmediately callsSubprocessTerminator.killProcessTree, which isprocess.kill(-pid, 'SIGKILL'). The script never receives SIGINT or SIGTERM. Dev servers leave lock/pid files and ports behind, and test runners skip teardown.RushXCommandthen reports the killed shell asError: An unknown error occurred., and the client exits 1 instead of 130/143.Repro steps
Take a rushx script that traps SIGINT/SIGTERM/SIGHUP, writes a marker file, and exits 40.
To its credit, the daemon kills the whole tree, so no orphans are left behind.
Expected result: Emulate a terminal Ctrl+C. Send the same signal (SIGINT/SIGTERM) to the script's process group, wait a bounded grace period (for example 2-5 s, within the client's cancellation timeout), then SIGKILL. The client exits
128 + signoand does not print a bogus "unknown error".Actual result: The script is killed immediately with SIGKILL, the client exits 1, and the message is misleading.
Details
Root cause (main @ 60007c9):
libraries/rush-daemon/src/GlobalCommandExecutionContext.ts:343-355(terminateChild) callskillProcessTree(SIGKILL) directly.CommandResultPolicy.ts:65-68maps the result to exit code 1.Suggested fix: add a graceful phase:
process.kill(-child.pid, requestSignal ?? 'SIGINT'), thenkillProcessTreeafter a grace timeout. Carry the client's signal inrequestCancel(for example an additive optionalsignalfield). Suppress the "unknown error" message for aborted requests, and return 130/143 (related: #6060, for phased builds).This was found during an automated performance/behavior analysis of
rush-client/rushdon Linux and independently reproduced.Standard questions
@microsoft/rushglobally installed version?main@ 60007c9 (5.179.0)rushVersionfrom rush.json?pnpmVersion,npmVersion, oryarnVersionfrom rush.json?useWorkspacesfrom pnpm-config.json?node -v)?