browsh-org/browsh · error
A headless Firefox is already running
Error message
A headless Firefox is already running
What it means
Browsh's interfacer refuses to start another headless Firefox when it detects an existing one. On non-Windows platforms `checkIfFirefoxIsAlreadyRunning` runs `ps aux` and matches any process line against the regex `firefox.*--headless`; a match means a headless instance (possibly a previous browsh run) is still alive, and starting a second one would conflict over the Web Extension connection and resources. The process calls `Shutdown`, terminating browsh.
Source
Thrown at interfacer/src/browsh/firefox.go:102
Shutdown(err)
}
if err := firefoxProcess.Start(); err != nil {
Shutdown(err)
}
in := bufio.NewScanner(stdout)
for in.Scan() {
slog.Info("FF-CONSOLE", "stdout", in.Text())
}
}
func checkIfFirefoxIsAlreadyRunning() {
if runtime.GOOS == "windows" {
return
}
processes := Shell("ps aux")
r, _ := regexp.Compile("firefox.*--headless")
if r.MatchString(processes) {
Shutdown(errors.New("A headless Firefox is already running"))
}
}
func ensureFirefoxBinary() string {
path := viper.GetString("firefox.path")
if path == "firefox" {
switch runtime.GOOS {
case "windows":
path = getFirefoxPath()
case "darwin":
path = "/Applications/Firefox.app/Contents/MacOS/firefox"
default:
path = getFirefoxPath()
}
}
if _, err := os.Stat(path); err != nil {
if errors.Is(err, fs.ErrNotExist) {
err = errors.New("Firefox binary not found: " + path)View on GitHub (pinned to 499ef386d4)
Solutions
- Find and kill the existing headless Firefox: `pkill -f 'firefox.*--headless'`, then retry.
- Run `ps aux | grep firefox` to confirm it is safe to kill before killing it.
- Avoid running multiple browsh instances simultaneously; serialize browsh invocations in scripts/CI.
- If browsh crashed and left orphans, fix cleanup (trap signals, use process groups) so Firefox is terminated with browsh.
Example fix
// before (shell, failing) browsh http://example.com # Error: A headless Firefox is already running // after pkill -f 'firefox.*--headless' || true browsh http://example.com
Defensive patterns
Strategy: validation
Validate before calling
const { execSync } = require('child_process');
const running = execSync('ps aux').toString().match(/firefox.*--headless/);
if (running) throw new Error('Kill existing headless Firefox before starting browsh'); Try / catch
try {
startBrowsh();
} catch (e) {
if (/headless Firefox is already running/.test(e.message)) {
execSync("pkill -f 'firefox.*--headless'");
startBrowsh();
} else { throw e; }
} Prevention
- pkill -f 'firefox.*--headless' before launching browsh in scripts/CI.
- Never run multiple browsh instances concurrently in the same environment.
- Use signal traps so browsh and its Firefox child die together on exit.
- When browsh won't start, inspect `ps aux | grep firefox` first.
When it happens
Trigger: Calling StartFirefox (via `startHeadlessFirefox` or `startWERFirefox`) when `ps aux` output contains a line matching `firefox.*--headless` — e.g. a leftover headless Firefox from a crashed or still-running browsh session. Never triggered on Windows (the check returns early).
Common situations: Previous browsh run didn't clean up its Firefox child process; a systemd/CI job left Firefox running; a script that starts browsh in a loop without killing the old instance; running two browsh instances concurrently in the same container.
Related errors
- There appears to already be an existing Web Extension connec
- Failed to connect to Firefox's Marionette within 30 seconds
- Firefox binary not found:
- Installed Firefox version is too old. Firefox 57 or newer i
- Could not find Firefox on your registry
AI-assisted analysis of browsh-org/browsh@499ef386d4 (2026-09-02).
Data as JSON: /api/errors/ddc5beec32ab38ac.
Report an issue: GitHub.