oraios/serena · error · RuntimeError

No free ports found starting from {start_port}

Error message

No free ports found starting from {start_port}

What it means

The dashboard's port-finding helper scans ports starting at start_port trying to bind; if it exhausts the range without finding a bindable port it raises RuntimeError('No free ports found starting from {start_port}'). Serena throws this to prevent starting the web dashboard on an occupied or restricted port.

Source

Thrown at src/serena/dashboard.py:808

        try:
            language = LanguageServerId(request_remove_language.language)
        except ValueError:
            raise ValueError(f"Invalid language server identifier: {request_remove_language.language}")
        # remove_language is already thread-safe
        self._agent.remove_language_server(language)

    @staticmethod
    def _find_first_free_port(start_port: int, host: str) -> int:
        port = start_port
        while port <= 65535:
            try:
                with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as sock:
                    sock.bind((host, port))
                    return port
            except OSError:
                port += 1

        raise RuntimeError(f"No free ports found starting from {start_port}")

    def run(self, port: int) -> int:
        """
        Runs the dashboard on the given host and port and returns the port number.
        """
        # patch flask.cli.show_server to avoid printing the server info
        from flask import cli

        # ty cannot model reassigning a third-party module's function attribute (it rejects any
        # replacement, even one with an identical signature), so the monkeypatch is suppressed here
        cli.show_server_banner = lambda *args, **kwargs: None  # ty: ignore[invalid-assignment]
        self._app.run(host=self._host, port=port, debug=False, use_reloader=False, threaded=True)
        return port

    def run_in_thread(self) -> tuple[threading.Thread, int]:
        port = self._find_first_free_port(self.BASE_PORT, self._host)
        log.info("Starting dashboard (listen_address=%s, port=%d)", self._host, port)
        thread = threading.Thread(target=lambda: self.run(port=port), daemon=True)

View on GitHub (pinned to 7fcbca7e62)

Solutions

  1. Find and stop the process holding the port (lsof -i :<port> / netstat) or kill the stale dashboard process
  2. Pass a different start_port when launching the dashboard
  3. Set the dashboard port explicitly in serena_config.yml to a known-free port
  4. Disable the dashboard (web_dashboard: false in config) if not needed

Example fix

// before
--port 24282  # occupied
// after
serena start --port 24300  # or set web_dashboard start_port in serena_config.yml
Defensive patterns

Strategy: validation

Validate before calling

import socket
def is_port_free(port: int, host: str = "localhost") -> bool:
    try:
        with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
            s.bind((host, port))
            return True
    except OSError:
        return False

if not any(is_port_free(p) for p in range(start_port, start_port + 100)):
    raise SystemExit(f"No free ports from {start_port}; free one or choose another range")

Try / catch

try:
    dashboard.run_in_thread(port)
except RuntimeError as e:
    if "No free ports" in str(e):
        port = find_alternate_port()  # or disable dashboard
    else:
        raise

Prevention

When it happens

Trigger: Calling Dashboard.run/run_in_thread when every port from start_port upward (within the scan range) is already bound, blocked by firewall, or lacks bind permission; another Serena/other-app instance already listening on the default port.

Common situations: Two Serena dashboard instances running concurrently; a stale process still holding the port; running in a container/CI where the port range is blocked; SELinux or non-root restrictions on low ports when start_port is small.

Related errors


AI-assisted analysis of oraios/serena@7fcbca7e62 (2026-08-29). Data as JSON: /api/errors/8632c5b5fb326099. Report an issue: GitHub.