nats-io/nats-server · error

can't listen to the monitor port: %v

Error message

can't listen to the monitor port: %v

What it means

The monitoring listener could not be bound: net.Listen("tcp", host:port) failed and the underlying OS error is wrapped. Typical causes are the port already in use, permission denied for privileged ports, or an unbindable host address.

Source

Thrown at server/server.go:3140

		hp = net.JoinHostPort(opts.HTTPHost, strconv.Itoa(port))
		config := opts.TLSConfig.Clone()
		if !s.ocspPeerVerify {
			config.GetConfigForClient = s.getMonitoringTLSConfig
			config.ClientAuth = tls.NoClientCert
		}
		httpListener, err = tls.Listen("tcp", hp, config)

	} else {
		port = opts.HTTPPort
		if port == -1 {
			port = 0
		}
		hp = net.JoinHostPort(opts.HTTPHost, strconv.Itoa(port))
		httpListener, err = net.Listen("tcp", hp)
	}

	if err != nil {
		return fmt.Errorf("can't listen to the monitor port: %v", err)
	}

	rport := httpListener.Addr().(*net.TCPAddr).Port
	s.Noticef("Starting %s monitor on %s", monitorProtocol, net.JoinHostPort(opts.HTTPHost, strconv.Itoa(rport)))

	mux := http.NewServeMux()

	// Root
	mux.HandleFunc(s.basePath(RootPath), s.HandleRoot)
	// Varz
	mux.HandleFunc(s.basePath(VarzPath), s.HandleVarz)
	// Connz
	mux.HandleFunc(s.basePath(ConnzPath), s.HandleConnz)
	// Routez
	mux.HandleFunc(s.basePath(RoutezPath), s.HandleRoutez)
	// Gatewayz
	mux.HandleFunc(s.basePath(GatewayzPath), s.HandleGatewayz)
	// Leafz

View on GitHub (pinned to 3a66a489d2)

Solutions

  1. Check and free the port: `lsof -i :8222` / `ss -ltnp`, stop the conflicting process or pick another port
  2. Bind to 0.0.0.0 or the loopback interface instead of an unresolvable host in `http_host`
  3. For ports <1024 run with the required capability/root or choose a high port
  4. Read the wrapped `%v` OS error to distinguish address-in-use vs permission vs no-route

Example fix

// before
http_host: "myhost.internal"
http_port: 8222
// after
http_host: "0.0.0.0"
http_port: 8222
Defensive patterns

Strategy: validation

Validate before calling

// Pre-check that the monitor port is bindable before starting the server
func portFree(host string, port int) error {
    ln, err := net.Listen("tcp", net.JoinHostPort(host, strconv.Itoa(port)))
    if err != nil { return err }
    return ln.Close()
}

Try / catch

if err := srv.StartMonitoring(); err != nil {
    var wrapped string = err.Error()
    if strings.Contains(wrapped, "can't listen to the monitor port") {
        if strings.Contains(wrapped, "address already in use") {
            log.Print("monitor port taken: stop the other process or change http_port")
        } else if strings.Contains(wrapped, "permission denied") {
            log.Print("use a port >1024 or grant CAP_NET_BIND_SERVICE")
        }
    }
    return err
}

Prevention

When it happens

Trigger: startMonitoring calls net.Listen for opts.HTTPPort/HTTPSPort (or the randomly allocated port) on opts.HTTPHost; any listen error (EADDRINUSE, EACCES, invalid host) triggers this.

Common situations: Another nats-server or old instance still holding 8222; binding to a hostname that doesn't resolve to a local interface; port <1024 without root; container missing CAP_NET_BIND_SERVICE.

Related errors


AI-assisted analysis of nats-io/nats-server@3a66a489d2 (2026-09-02). Data as JSON: /api/errors/73d8a4b8fabfabdd. Report an issue: GitHub.