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
Windows: emulators launched with shell: true open a visible console window (detached spawn without windowsHide) #11261
firebase-tools: 15.33.0 (behavior unchanged on main)
Platform: Windows (reproduced on GitHub Actions windows-latest)
[REQUIRED] Test case
Two small Node scripts and no Firebase project needed. launcher.cjs uses the same spawn options that _runBinary in src/emulator/downloadableEmulators.ts uses for an emulator whose command has shell: true (today that is the Pub/Sub emulator):
// leaf.cjs: stands in for the emulator's console programconsole.log("leaf pid",process.pid);setTimeout(()=>{},30000);
[REQUIRED] Steps to reproduce
On Windows, start node launcher.cjs from a process that has no console of its own, such as an IDE, an editor's task runner, a GUI app, an agent host, or a service. (When run from an interactive terminal the child shares that terminal's console, so the bug is hidden.)
Observe a new console window for the leaf process.
Run node launcher.cjs hidden the same way. The window still appears: windowsHide alone does not fix this (see below).
We checked this on CI with AttachConsole(pid) + GetConsoleWindow() + IsWindowVisible() against the leaf pid:
with today's options (detached: true, shell: true): the leaf owns a console window and IsWindowVisible is true;
the same options plus windowsHide: true: the leaf still owns a visible console window (see the correction comment below);
the same detached spawn without shell: true (the form the Java emulators use): the leaf owns no console at all, so no window.
[REQUIRED] Expected behavior
Emulators start without opening a console window, as firebase emulators:exec already does for the user's script: src/emulator/commandUtils.ts spawns it with shell: true, windowsHide: true.
[REQUIRED] Actual behavior
With detached: true (DETACHED_PROCESS), the shell child does not inherit the parent's console, so the console program it launches allocates a fresh, visible console window. That window takes focus. This looks like the cause of #3506 ("PubSub Emulator on Windows launches a Java window"), which became the default path when #7033 switched the Pub/Sub launch to shell: true on Windows.
Proposed fix
Not settled yet. windowsHide: true alone does not help: with detached: true, the cmd.exe that shell: true starts gets no console, so the console program it launches (the .bat's java.exe) allocates a new, visible one. windowsHide applies to cmd.exe, not to the program it starts. We're measuring which launch options avoid the window while keeping the reason detached is set (graceful SIGINT handling), and will post the results here.
Correction to the original post: I claimed that adding windowsHide: true hides the window, and proposed that as a one-line fix. That was wrong. I had reasoned it out and had not yet run that variant.
The measurement, on GitHub Actions windows-latest from a console-less parent, with the leaf checked via AttachConsole + GetConsoleWindow + IsWindowVisible:
spawn options
leaf has a visible console window
detached: true, shell: true (today's _runBinary for shell commands)
yes
detached: true, shell: true, windowsHide: true
yes
detached: true, no shell (the Java emulators' form)
no (the leaf has no console at all)
So the bug is real, but windowsHide is not the fix: with DETACHED_PROCESS, the intermediate cmd.exe has no console, and the console program it starts creates a fresh, visible one. I've updated the issue body. I'll follow up with the options that do avoid the window (for example a non-detached hidden launch, or starting the emulator without the .bat/shell layer) once they're measured, rather than guessing again. Sorry for the noise.
Measured results, as promised. GitHub Actions windows-latest, launched from a console-less parent. The leaf (the console program that cmd.exe or the launcher starts) was checked with AttachConsole + GetConsoleWindow + IsWindowVisible. All rows come from one run.
spawn options for the leaf launcher (stdio: ["inherit", "pipe", "pipe"] throughout)
leaf has a console
leaf window visible
shell: true, detached: true (today's _runBinary for shell commands)
yes
yes
shell: true, detached: true, windowsHide: true
yes
yes
shell: true, detached: false, windowsHide: true
yes, hidden
no
no shell, detached: true (the Java emulators' current form)
none
no
no shell, detached: true, windowsHide: true
none
no
no shell, detached: false, windowsHide: true
yes, hidden
no
What this shows:
The window appears only when shell: true and detached: true are combined. windowsHide does not change that, because it applies to the intermediate cmd.exe, and DETACHED_PROCESS leaves cmd.exe without a console for its child to inherit.
Launching without the shell layer (as the Java emulators already do) avoids the window even with detached: true.
Keeping the shell but dropping detached (with windowsHide: true) also avoids it, but detached is there for the SIGINT/graceful-shutdown reason noted in _runBinary, so that trade-off is yours to judge.
I'm not proposing a specific patch here; the table is meant to inform whichever direction you prefer for #7033's .bat launch.
Activity
test(coding-agent): document the firebase-tools detached shell window…
code-yeongyu commentedon Oct 10, 2026
Correction to the original post: I claimed that adding
windowsHide: truehides the window, and proposed that as a one-line fix. That was wrong. I had reasoned it out and had not yet run that variant.The measurement, on GitHub Actions
windows-latestfrom a console-less parent, with the leaf checked viaAttachConsole+GetConsoleWindow+IsWindowVisible:detached: true, shell: true(today's_runBinaryfor shell commands)detached: true, shell: true, windowsHide: truedetached: true, no shell (the Java emulators' form)So the bug is real, but
windowsHideis not the fix: withDETACHED_PROCESS, the intermediatecmd.exehas no console, and the console program it starts creates a fresh, visible one. I've updated the issue body. I'll follow up with the options that do avoid the window (for example a non-detached hidden launch, or starting the emulator without the.bat/shell layer) once they're measured, rather than guessing again. Sorry for the noise.test(coding-agent): measure firebase-tools launch options for omo#769…
code-yeongyu commentedon Oct 10, 2026
Measured results, as promised. GitHub Actions
windows-latest, launched from a console-less parent. The leaf (the console program thatcmd.exeor the launcher starts) was checked withAttachConsole+GetConsoleWindow+IsWindowVisible. All rows come from one run.stdio: ["inherit", "pipe", "pipe"]throughout)shell: true, detached: true(today's_runBinaryfor shell commands)shell: true, detached: true, windowsHide: trueshell: true, detached: false, windowsHide: truedetached: true(the Java emulators' current form)detached: true, windowsHide: truedetached: false, windowsHide: trueWhat this shows:
shell: trueanddetached: trueare combined.windowsHidedoes not change that, because it applies to the intermediatecmd.exe, andDETACHED_PROCESSleavescmd.exewithout a console for its child to inherit.detached: true.detached(withwindowsHide: true) also avoids it, butdetachedis there for the SIGINT/graceful-shutdown reason noted in_runBinary, so that trade-off is yours to judge.I'm not proposing a specific patch here; the table is meant to inform whichever direction you prefer for #7033's
.batlaunch.