browsh-org/browsh · critical

Error starting websocket server: %w

Error message

Error starting websocket server: %w

What it means

Browsh's interfacer runs an HTTP/WebSocket server (on `browsh.websocket-port`, default 3333) that the Web Extension connects to. `startWebSocketServer` calls `http.ListenAndServe`; if it returns an error, browsh shuts down wrapping it with this message. In practice this is almost always a port conflict or permission problem.

Source

Thrown at interfacer/src/browsh/comms.go:36

		ReadBufferSize:  1024,
		WriteBufferSize: 1024,
	}
	stdinChannel              = make(chan string)
	IsConnectedToWebExtension = false
)

type incomingRawText struct {
	RequestID string `json:"request_id"`
	RawJSON   string `json:"json"`
}

func startWebSocketServer() {
	serverMux := http.NewServeMux()
	serverMux.HandleFunc("/", webSocketServer)
	port := viper.GetString("browsh.websocket-port")
	slog.Info("Starting websocket server...")
	if netErr := http.ListenAndServe(":"+port, serverMux); netErr != nil {
		Shutdown(fmt.Errorf("Error starting websocket server: %w", netErr))
	}
}

func sendMessageToWebExtension(message string) {
	if !IsConnectedToWebExtension {
		slog.Info("Webextension not connected. Message not sent", "message", message)
		return
	}
	stdinChannel <- message
}

// Listen to all messages coming from the webextension
// TODO: It seems this *also* receives sent to the webextention!?
func webSocketReader(ws *websocket.Conn) {
	defer ws.Close()
	for {
		_, message, err := ws.ReadMessage()
		handleWebextensionCommand(message)

View on GitHub (pinned to 499ef386d4)

Solutions

  1. Check what holds the port: `lsof -i :3333` (or your configured port) and kill the stale process.
  2. Change the port: set `browsh.websocket-port = <free-port>` in your browsh config.
  3. Verify no firewall/container restriction blocks binding to the port.
  4. Restart cleanly so no orphaned browsh keeps the websocket port bound.

Example fix

// before (config .toml)
websocket-port = "3333"  # already in use
// after
websocket-port = "3334"
Defensive patterns

Strategy: validation

Validate before calling

const net = require('net');
const port = 3333; // browsh.websocket-port
const probe = net.createServer();
probe.once('error', () => { throw new Error(`Port ${port} already in use`); });
probe.listen(port, () => { probe.close(() => console.log('port free')); });

Try / catch

try {
  startBrowsh();
} catch (e) {
  if (/Error starting websocket server/.test(e.message)) {
    console.error('Free the port or change browsh.websocket-port:', e.message);
  } else { throw e; }
}

Prevention

When it happens

Trigger: `TTYStart` or `HTTPServerStart` invokes `startWebSocketServer`; `http.ListenAndServe(":"+port, mux)` fails because the port is already bound (another browsh/websocket server), is privileged (<1024), or the configured port value is invalid.

Common situations: A previous browsh instance still running and holding port 3333; another app using the configured port; `browsh.websocket-port` misconfigured in the .toml; running in a container without the port exposed/permitted.

Related errors


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