[Bug]: dev-electron supervisor survives its parent and keeps restarting Electron#19109

opened29d agobyRrenepf

Before submitting

  • I searched existing issues and did not find a duplicate.
  • I included enough detail to reproduce or investigate the problem.

Area

apps/desktop

Steps to reproduce

  1. Start the desktop dev app from a checkout: vp run dev:desktop --home-dir <some dev home>.
  2. Kill the parent vp process without letting it forward a signal — kill -9 <vp pid>, or close a terminal that dies before it can signal its children.
  3. Collect the supervisor PIDs and check their parent:
    1ps -o pid=,ppid=,command= -ax | grep 'dev-electron.mjs'
  4. Touch one of the watched build artifacts: touch apps/desktop/dist-electron/main.cjs.
  5. Repeat steps 1 and 2 a few times without cleaning up in between.

Expected behavior

This is the maintainers' call. Two directions, offered for discussion rather than as a proposal: have the supervisor exit when its parent goes away, and/or extend cleanupStaleDevApps() to reap stale supervisors of the same dev root as well as their apps.

Actual behavior

apps/desktop/scripts/dev-electron.mjs handles SIGINT, SIGTERM and SIGHUP and shuts down cleanly (:261-269). It has no parent-death watch, so when the parent vp process dies without forwarding a signal the supervisor is reparented to ppid 1 and keeps restarting Electron on the next file change (:188-202, debounce :50).

cleanupStaleDevApps() (:92-100, called at startup on :258 and twice inside stopApp(), at :168 and :177 — so on every debounced restart via :197, not only on shutdown via :247) only clears stale Electron apps of the same dev root, via pkill -f -- --t3code-dev-root=<desktopDir>. It does not clear stale supervisors. Several orphans therefore coexist, each restarting Electron and each killing the others' window on its own startup, all against the same T3 home.

Stopping the three orphans by their captured PIDs let the next run start cleanly with the same database: POST /oauth/token returned 200 once and zero SessionBootstrapError.

The 401 that surfaces while orphans are running is filed separately as #19110: this issue is the stale process, that one is why the symptom is undiagnosable.

One note while reading :97: that line uses pkill -f with the worktree path as the pattern, which is the exact shape AGENTS.md forbids contributors in "The three ways to hurt yourself", point 1 ("Your own agent process has this worktree's path in its argv"). Whether the script itself should be held to that rule is a separate question; flagging it because an agent that reads AGENTS.md and then this script gets contradictory guidance.

Impact

Major degradation or frequent failure

Version or commit

7b6d907574

Environment

macOS 26.3.2 (Darwin 25.3.0), Apple Silicon; Node 24.13.1; pnpm 11.10.0; vp 1.1.0 — the global install resolved from the bare vp in step 1 (command -v vp → /opt/homebrew/bin/vp, vp --version → vp v1.1.0). The checkout also ships its own ./node_modules/.bin/vp, which reports vp v1.0.0; that one was not used for this run.

Logs or stack traces

shell
1$ ps -p <pid> -o ppid=
2# three `node .../dev-electron.mjs` processes, each reporting ppid 1
3
4# after stopping those three by their captured PIDs, same run, same database:
5# POST /oauth/token -> 200 (1 occurrence)
6# SessionBootstrapError -> 0 occurrences

Workaround

Before each start, check that no dev-electron.mjs is running and that the dev ports are free, and stop a run by the PID of the vp process plus the supervisor PIDs collected at startup.

Note

Grok responding on behalf of Julius.

Confirmed on main. This only affects the desktop dev workflow (dev:desktop), not shipped builds. Production backend orphaning is tracked separately in #13747.

What main does in apps/desktop/scripts/dev-electron.mjs:

  • The supervisor only exits on SIGINT/SIGTERM/SIGHUP (:261-269). Nothing watches for parent death, so after a kill -9 of vp it is reparented to PID 1, and the next file change still runs scheduleRestart() (:183).
  • cleanupStaleDevApps() (:92-100) only runs pkill -f -- --t3code-dev-root=…, which matches Electron apps and not other supervisors. Orphaned supervisors therefore pile up and fight over the same T3 home. #19110 is the 401 this causes.

The workaround is in the report: before restarting, kill any leftover dev-electron.mjs processes.

Closed PR #8736 already addresses the root cause. It moves the supervisor under dev-runner as a sibling process group and drops the pkill -f cleanup. It was closed during a stale-PR sweep and not on its merits, so it's the likely starting point for a fix. Keeping this open as a dev-tooling bug.