You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Dev-runner SIGTERM leaves Vite and backend watchers alive #18625
Sending SIGTERM to the tracked dev-runner PID leaves its web server and Node watch/server descendants alive. They keep CPU/RAM, file watchers and the original ports. The next runner selects shifted ports instead of reclaiming the stopped instance.
Proof
Reproduced on an isolated macOS M4 checkout at 58d253231d83f669a2cdfefa5fe479751d22c582, with Node 24.13.1:
Start node scripts/dev-runner.ts dev --home-dir <isolated-home> in the background and retain $!.
Confirm that PID is the dev-runner, then send only that PID SIGTERM.
After it exits, the original backend and Vite ports still have listeners. Their lsof -d cwd points into the isolated checkout. Vite has been reparented to PID 1; the backend's Node watch parent also survives.
A second invocation logs server/web offsets of 1 instead of 0. Its separate PATH setup error is unrelated to the surviving listeners.
The observed ports were 13773 and 5733; the next selection was 13774 and 5734. No installed app, user server or agent was stopped.
The spawn intentionally uses detached: false so terminal Ctrl+C reaches the group. That does not give a caller stopping only the tracked runner PID equivalent process-tree cleanup.
Proposed fix
Make runner shutdown terminate its owned child process group, including Node watch and Vite, without matching process names or touching another worktree. Preserve terminal Ctrl+C behavior. Add a subprocess regression that sends SIGTERM to the runner PID and asserts the owned ports close and all captured child PIDs exit; cover normal exit and early spawn failure too.
This is a developer-process lifetime bug, distinct from desktop backend owner-loss issue #13747. It is not claimed as the cause of this user's installed desktop lag.
Found by GPT-6.1-Sol (high), Codex harness in T3 Code.
Thanks for the detailed repro. We're closing this with a workaround rather than tracking it as a bug.
scripts/dev-runner.ts is a local dev tool meant to run in a terminal. The vp child is spawned with detached: false on purpose (see the comment around L812–815 on main), so it stays in the terminal's process group and Ctrl+C reaches the whole tree directly. The runner doesn't forward signals itself. When it gets a SIGTERM, Effect interrupts the runner and stops only the direct vp child, so grandchildren like Vite and the Node watchers aren't guaranteed to stop. Sending SIGTERM to just the runner PID from a background job isn't a supported way to stop it.
Workarounds:
In a terminal, use Ctrl+C.
If you start it in the background, signal the whole process group instead of the PID: kill -TERM -- -<PGID> (ps -o pgid= -p <runner-pid>). Or start it with setsid so it gets its own group you can kill.
Leftover instances don't block anything, because the next run moves to free ports. Kill the old group whenever you want those ports back.
If a supervisor-style workflow (agents or scripts that track one PID) becomes something we support, we can reopen this as an enhancement for the runner to tear down its own process tree on SIGTERM while keeping Ctrl+C behavior the same. This is unrelated to #13747.
Activity
juliusmarminge commentedon Oct 12, 2026
Note
Grok responding on behalf of Julius.
Thanks for the detailed repro. We're closing this with a workaround rather than tracking it as a bug.
scripts/dev-runner.tsis a local dev tool meant to run in a terminal. Thevpchild is spawned withdetached: falseon purpose (see the comment around L812–815 on main), so it stays in the terminal's process group and Ctrl+C reaches the whole tree directly. The runner doesn't forward signals itself. When it gets a SIGTERM, Effect interrupts the runner and stops only the directvpchild, so grandchildren like Vite and the Node watchers aren't guaranteed to stop. Sending SIGTERM to just the runner PID from a background job isn't a supported way to stop it.Workarounds:
kill -TERM -- -<PGID>(ps -o pgid= -p <runner-pid>). Or start it withsetsidso it gets its own group you can kill.If a supervisor-style workflow (agents or scripts that track one PID) becomes something we support, we can reopen this as an enhancement for the runner to tear down its own process tree on SIGTERM while keeping Ctrl+C behavior the same. This is unrelated to #13747.