browsh-org/browsh · critical
Failed to connect to Firefox's Marionette within 30 seconds
Error message
Failed to connect to Firefox's Marionette within 30 seconds
What it means
Browsh controls Firefox over its Marionette/WebDriver port. `firefoxMarionette` retries dialing that port in a loop for up to 30 seconds; if no connection can be established by then, it concludes Firefox never exposed Marionette and calls `Shutdown`. This means Firefox either didn't start, started without `-marionette`, or is unreachable at the configured host/port.
Source
Thrown at interfacer/src/browsh/firefox.go:230
connected := false
slog.Info("Attempting to connect to Firefox Marionette")
start := time.Now()
for time.Since(start) < 30*time.Second {
conn, err = net.Dial("tcp", "127.0.0.1:2828")
if err != nil {
if !strings.Contains(err.Error(), "refused") {
Shutdown(err)
} else {
time.Sleep(10 * time.Millisecond)
continue
}
} else {
connected = true
break
}
}
if !connected {
Shutdown(errors.New("Failed to connect to Firefox's Marionette within 30 seconds"))
}
marionette = conn
go readMarionette()
sendFirefoxCommand("WebDriver:NewSession", map[string]interface{}{})
}
func installWebextension() {
data, err := browshXpi.ReadFile("browsh.xpi")
if err != nil {
Shutdown(err)
}
path := path.Join(os.TempDir(), "browsh-webext-addon")
if err := os.WriteFile(path, []byte(data), 0644); err != nil {
Shutdown(err)
}
args := map[string]interface{}{"path": path}
sendFirefoxCommand("Addon:Install", args)
}View on GitHub (pinned to 499ef386d4)
Solutions
- Verify Firefox starts manually with Marionette: run the same command with `-marionette` and check it doesn't crash.
- Confirm the Marionette port (default 2828) is free and reachable: `ss -tlnp | grep 2828`.
- Retry on a faster/less loaded machine or warm up Firefox before timing-sensitive runs; check for missing shared libraries (`ldd $(which firefox)`).
- Inspect the launched Firefox's stderr/logs for immediate crash causes (profile lock, missing libraries, no display).
Example fix
// before (shell check) firefox --headless # starts without marionette -> 30s timeout // after firefox --headless --marionette # marionette listens on :2828 ss -tlnp | grep 2828 # confirm listening before running browsh
Defensive patterns
Strategy: retry
Validate before calling
const net = require('net');
const s = net.connect(2828, '127.0.0.1');
s.on('connect', () => { console.log('marionette up'); s.end(); });
s.on('error', () => console.error('marionette not listening; firefox may have failed to start')); Try / catch
try {
startBrowsh();
} catch (e) {
if (/Marionette within 30 seconds/.test(e.message)) {
// warm-up retry: give slow machines a chance
setTimeout(startBrowsh, 5000);
} else { throw e; }
} Prevention
- Verify `firefox --headless --marionette` starts manually before automation.
- Confirm port 2828 is open and not firewalled.
- Check Firefox can launch in the environment (libs, profile, display).
- On slow CI machines, warm up Firefox or reduce startup load.
When it happens
Trigger: `setupFirefox` or `StartFirefox` calls `firefoxMarionette`; the dial loop never succeeds within 30s — Firefox failed to launch (bad binary/flags), crashed immediately on startup, started too slowly (cold start under load), or Marionette is disabled/bound elsewhere.
Common situations: Slow/loaded CI machines where Firefox takes >30s to start; Firefox crashing on launch due to missing X/GTK libs or profile issues; running a Firefox build with Marionette disabled; resource-starved containers hitting startup timeouts.
Related errors
- A headless Firefox is already running
- There appears to already be an existing Web Extension connec
- 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/bde33158a340d668.
Report an issue: GitHub.