{"record":{"id":"8632c5b5fb326099","repo":"oraios/serena","slug":"no-free-ports-found-starting-from-start-port","errorCode":null,"errorMessage":"No free ports found starting from {start_port}","messagePattern":"No free ports found starting from (.+?)","errorType":"exception","errorClass":"RuntimeError","httpStatus":null,"severity":"error","filePath":"src/serena/dashboard.py","lineNumber":808,"sourceCode":"        try:\n            language = LanguageServerId(request_remove_language.language)\n        except ValueError:\n            raise ValueError(f\"Invalid language server identifier: {request_remove_language.language}\")\n        # remove_language is already thread-safe\n        self._agent.remove_language_server(language)\n\n    @staticmethod\n    def _find_first_free_port(start_port: int, host: str) -> int:\n        port = start_port\n        while port <= 65535:\n            try:\n                with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as sock:\n                    sock.bind((host, port))\n                    return port\n            except OSError:\n                port += 1\n\n        raise RuntimeError(f\"No free ports found starting from {start_port}\")\n\n    def run(self, port: int) -> int:\n        \"\"\"\n        Runs the dashboard on the given host and port and returns the port number.\n        \"\"\"\n        # patch flask.cli.show_server to avoid printing the server info\n        from flask import cli\n\n        # ty cannot model reassigning a third-party module's function attribute (it rejects any\n        # replacement, even one with an identical signature), so the monkeypatch is suppressed here\n        cli.show_server_banner = lambda *args, **kwargs: None  # ty: ignore[invalid-assignment]\n        self._app.run(host=self._host, port=port, debug=False, use_reloader=False, threaded=True)\n        return port\n\n    def run_in_thread(self) -> tuple[threading.Thread, int]:\n        port = self._find_first_free_port(self.BASE_PORT, self._host)\n        log.info(\"Starting dashboard (listen_address=%s, port=%d)\", self._host, port)\n        thread = threading.Thread(target=lambda: self.run(port=port), daemon=True)","sourceCodeStart":790,"sourceCodeEnd":826,"githubUrl":"https://github.com/oraios/serena/blob/7fcbca7e62555ec2287ddb2f083caee805848ea6/src/serena/dashboard.py#L790-L826","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Find and stop the process holding the port (lsof -i :<port> / netstat) or kill the stale dashboard process","Pass a different start_port when launching the dashboard","Set the dashboard port explicitly in serena_config.yml to a known-free port","Disable the dashboard (web_dashboard: false in config) if not needed"],"exampleFix":"// before\n--port 24282  # occupied\n// after\nserena start --port 24300  # or set web_dashboard start_port in serena_config.yml","handlingStrategy":"validation","validationCode":"import socket\ndef is_port_free(port: int, host: str = \"localhost\") -> bool:\n    try:\n        with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:\n            s.bind((host, port))\n            return True\n    except OSError:\n        return False\n\nif not any(is_port_free(p) for p in range(start_port, start_port + 100)):\n    raise SystemExit(f\"No free ports from {start_port}; free one or choose another range\")","typeGuard":null,"tryCatchPattern":"try:\n    dashboard.run_in_thread(port)\nexcept RuntimeError as e:\n    if \"No free ports\" in str(e):\n        port = find_alternate_port()  # or disable dashboard\n    else:\n        raise","preventionTips":["Check port availability with a socket bind before starting","Configure a distinct dashboard port per project in serena_config.yml","Kill stale dashboard processes after crashes (lsof -i :<port>)","Disable the web dashboard in CI/container environments"],"tags":["python","ports","network","dashboard"],"backgroundTag":"port-already-in-use","analyzedSha":"7fcbca7e62555ec2287ddb2f083caee805848ea6","analyzedAt":"2026-08-29T00:04:09.619Z","schemaVersion":2},"datasetVersion":"2026-08-29T02:17:18.158Z"}