{"record":{"id":"04e271e3260b5149","repo":"HKUDS/nanobot","slug":"public-ws-url-must-be-an-absolute-ws-or-wss","errorCode":null,"errorMessage":"public_ws_url must be an absolute ws:// or wss:// URL without credentials","messagePattern":"public_ws_url must be an absolute ws:// or wss:// URL without credentials","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"nanobot/channels/websocket/runtime.py","lineNumber":268,"sourceCode":"            raise ValueError('token_issue_path must start with \"/\"')\n        return _normalize_config_path(value)\n\n    @field_validator(\"public_ws_url\")\n    @classmethod\n    def public_ws_url_format(cls, value: str) -> str:\n        value = value.strip()\n        if not value:\n            return \"\"\n        parsed = urlsplit(value)\n        if (\n            parsed.scheme not in {\"ws\", \"wss\"}\n            or not parsed.netloc\n            or parsed.username is not None\n            or parsed.password is not None\n            or parsed.query\n            or parsed.fragment\n        ):\n            raise ValueError(\"public_ws_url must be an absolute ws:// or wss:// URL without credentials\")\n        return urlunsplit(\n            (parsed.scheme, parsed.netloc, _normalize_config_path(parsed.path or \"/\"), \"\", \"\")\n        )\n\n    @model_validator(mode=\"after\")\n    def public_ws_url_matches_path(self) -> Self:\n        if self.public_ws_url and urlsplit(self.public_ws_url).path != _normalize_config_path(self.path):\n            raise ValueError(\"public_ws_url path must match path\")\n        return self\n\n    @model_validator(mode=\"after\")\n    def token_issue_path_differs_from_ws_path(self) -> Self:\n        if not self.token_issue_path:\n            return self\n        if _normalize_config_path(self.token_issue_path) == _normalize_config_path(self.path):\n            raise ValueError(\"token_issue_path must differ from path (the WebSocket upgrade path)\")\n        return self\n","sourceCodeStart":250,"sourceCodeEnd":286,"githubUrl":"https://github.com/HKUDS/nanobot/blob/42f37dc4c0e409eac7818a82d165c5f2757bc075/nanobot/channels/websocket/runtime.py#L250-L286","documentation":"In WebSocketConfig.public_ws_url_format, the configured public_ws_url is non-empty but failed the strict URL check: it must be an absolute ws:// or wss:// URL with a netloc, no embedded username/password, and no query or fragment. The input at fault is the public_ws_url string (wrong scheme, missing host, credentials, or trailing query/hash).","triggerScenarios":"Thrown at nanobot/channels/websocket/runtime.py:268 when the library encounters an invalid state.","commonSituations":"See trigger scenarios.","solutions":["Use form wss://example.com/ws (or ws:// for plain) with explicit host","Remove any user:pass@, ?query, or #fragment parts","Leave public_ws_url empty to derive the URL from proxy request headers"],"exampleFix":null,"handlingStrategy":"validation","validationCode":null,"typeGuard":null,"tryCatchPattern":null,"preventionTips":[],"tags":[],"backgroundTag":null,"analyzedSha":"42f37dc4c0e409eac7818a82d165c5f2757bc075","analyzedAt":"2026-08-26T00:18:52.276Z","contentChangedAt":"2026-08-26T00:18:52.276Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}