[Bug] Desktop leaks ELECTRON_RUN_AS_NODE into every child process - Electron apps launched from a DSH shell start as Node #8174
Replies: 1 comment
|
The diagnosis is right, and the presence-semantics detail ( The two omissions Both runner invocations are
Why a tombstone in the shared scrub breaks them On Windows the subprocess runner is launched through // runner-launch.ts:68-86
const env = childEnv() // <- the scrub you are changing
for (const name of Object.keys(env)) { // strips only RUNNER_CONTROL_ENV_PREFIXES
if (RUNNER_CONTROL_ENV_PREFIXES.some(p => normalized.startsWith(p))) Reflect.deleteProperty(env, name)
}
return { ...env, [SUBPROCESS_RUNNER_ENV]: selection, SYSTEMD_LOG_TARGET: 'null', ... }
Independent measurement, same host, three days later #8193 captured the Desktop process tree live (30 ms polling of
Suggested adjustment Keep the tombstone in the shared base (it is the right place for "not inherited"), and preserve the selector explicitly where a runner is the child — one entry in Worth noting the precedent you cite is deliberately weaker than the suggested tombstone, and in a useful way: On Related, not a fix for this. |
Uh oh!
There was an error while loading. Please reload this page.
Summary
apps/desktopdeliberately runs the Harness under Electron withELECTRON_RUN_AS_NODE=1. That variable is a process-wide selector, not data: Electron treats it as present-or-absent (even""/0/falseenable Node mode). It is not scrubbed from the environment handed to child processes, so it is inherited by shell tools, terminal (PTY) sessions, MCP servers, LSP servers, subagent CLIs, etc.Consequence: any Electron application launched from a DSH shell starts as a plain Node process — no window, and its main process typically throws (
appisundefined,require('electron')returns a path string). This breaks every Electron project a developer tries to start (npm run dev,electron .,electron-vite,electron-forge, or an already-packaged.exe), and the failure is silent (exit 0or a confusingTypeError), so it costs a lot of time to diagnose.Reproduction
Windows, DSH desktop (nightly). Inside a DSH shell, with any Electron project:
Also reproduces with a packaged build: launching the built
*.exefrom a DSH shell exits immediately with code 0 and no window. Launching the same.exefrom Explorer, or after clearing the variable in the shell, works normally.Workaround that fixes it (on the user side only):
Note:
$env:ELECTRON_RUN_AS_NODE = ''does not work in PowerShell, andcross-env ELECTRON_RUN_AS_NODE=cannot work either, because Electron checks presence rather than value — only actually removing the variable helps.Current behavior
Every child spawned through the shared scrub base inherits the selector:
packages/subprocess/subprocess/src/index.ts—SENSITIVE_ENV_PATTERN = /KEY|PASSWORD|SECRET|TOKEN/iandscrubbedParentEnv(); only credential-shaped names andDSH_*are dropped, soELECTRON_RUN_AS_NODEpasses through.packages/subprocess/subprocess-local/src/spawn.ts/runner-launch.tsbuild every ordinary child env from that base.packages/shell/pwsh-local/src/index.ts(env: { ...ENV_OVERRIDES, ...spec.env, ...spec.dshEnv }) andpackages/terminal/terminal-bash/src/index.ts("The subprocess provider supplies its own scrubbed ambient base…").open-in-appspawn paths.So the leak is not specific to one tool: it is the environment policy of the whole subprocess seam.
Expected behavior
Children should not inherit the harness's own Node-mode selector. Every deliberate consumer already passes it explicitly in
env, so nothing depends on inheritance:apps/desktop/src/node-environment.ts(desktopNodeEnvironment()returns{ ...environment, ELECTRON_RUN_AS_NODE: '1', … })apps/desktop-host/src/index.ts(package manager launch)apps/desktop/scripts/node-bin/node.cmd/node(sets it in the launcher itself)packages/host/open-in-app/src/resolver.ts(Electron-based CLIs)packages/experimental/speech-to-text-sensevoice/src/recognizer.tsAnd there is already a precedent for treating it as something to strip:
packages/ptc-runtime/ptc-runtime-node/src/index.tsfilters it out of the child environment, with the documented rationale "The host preservesELECTRON_RUN_AS_NODEonly for child startup so the Desktop executable runs the Node bootstrap; the bootstrap removes the selector before evaluating model code" (see that package's README andtests/host-failures.spec.ts, which asserts the variable is absent).Suggested fix
Make the shared scrub tombstone the selector, matching the
ptc-runtime-nodeprecedent:Explicit
envlayers merge after the scrub (childEnv(extra)), so all deliberate uses above keep working unchanged — only passive inheritance is cut.NODE_OPTIONSis the same class of variable and is worth considering together.Environment
master@0.2.0-rc.1(source-verified).All reactions