browsh-org/browsh · critical

Firefox binary not found:

Error message

Firefox binary not found: 

What it means

`ensureFirefoxBinary` resolves the Firefox executable path (from `firefox.path` config or platform-specific lookup) and stats it. If the file does not exist, browsh cannot launch Firefox at all, so it wraps the path into this error and calls `Shutdown`. The message includes the resolved path, so a trailing colon with an empty value means the platform lookup returned an empty path.

Source

Thrown at interfacer/src/browsh/firefox.go:120

		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)
		}
		Shutdown(err)
	}
	slog.Info("Using Firefox", "path", path)
	return path
}

// Taken from https://stackoverflow.com/a/18411978/575773
func versionOrdinal(version string) string {
	// ISO/IEC 14651:2011
	const maxByte = 1<<8 - 1
	vo := make([]byte, 0, len(version)+8)
	j := -1
	for i := 0; i < len(version); i++ {
		b := version[i]
		if '0' > b || b > '9' {
			vo = append(vo, b)
			j = -1

View on GitHub (pinned to 499ef386d4)

Solutions

  1. Set `firefox.path` in your browsh config (.toml) to the full path of a Firefox binary (e.g. `/usr/bin/firefox`).
  2. Verify with `which firefox` or `ls <path>` that the binary actually exists and is executable.
  3. If using snap/flatpak Firefox, install a native (deb/rpm) Firefox or point firefox.path at the real binary path inside the snap/flatpak.
  4. Install Firefox: `apt install firefox` (or equivalent), then retry browsh.

Example fix

// before (config .toml)
[firefox]
path = "/opt/firefox/firefox-bin"  # does not exist
// after
[firefox]
path = "/usr/bin/firefox"
Defensive patterns

Strategy: validation

Validate before calling

const fs = require('fs');
const path = '/usr/bin/firefox'; // same value as firefox.path config
if (!fs.existsSync(path)) throw new Error(`Firefox binary missing at ${path}`);

Type guard

function firefoxPathIsValid(p) {
  return typeof p === 'string' && p.length > 0 && fs.existsSync(p);
}

Try / catch

try {
  startBrowsh();
} catch (e) {
  if (/Firefox binary not found/.test(e.message)) {
    console.error('Set firefox.path in config to an existing binary:', e.message);
  } else { throw e; }
}

Prevention

When it happens

Trigger: `os.Stat(path)` returns an error with `fs.ErrNotExist` during `startHeadlessFirefox`. Happens when the `firefox.path` config value points to a nonexistent binary, or the platform lookup (`getFirefoxPath`) finds nothing (empty path).

Common situations: Firefox not installed or installed under a name the lookup doesn't cover (snap/flatpak installs); typo'd `firefox.path` in the .toml config; headless servers with no Firefox; flatpak Firefox where the binary isn't on PATH.

Related errors


AI-assisted analysis of browsh-org/browsh@499ef386d4 (2026-09-02). Data as JSON: /api/errors/3fbbb0126af266c2. Report an issue: GitHub.