{"record":{"id":"d769385d2a244047","repo":"xai-org/grok-build","slug":"websocket-url-has-no-host","errorCode":null,"errorMessage":"WebSocket URL has no host","messagePattern":"WebSocket URL has no host","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/codegen/xai-grok-shell/src/agent/relay.rs","lineNumber":421,"sourceCode":"/// CONNECT tunnel.  Otherwise, a direct connection is used.\nasync fn connect_to_relay(\n    config: &RelayConfig,\n    proxy_url: Option<&str>,\n    cancel: &CancellationToken,\n) -> anyhow::Result<\n    tokio_tungstenite::WebSocketStream<tokio_tungstenite::MaybeTlsStream<tokio::net::TcpStream>>,\n> {\n    let req = build_relay_request(config)?;\n    let connect_timeout = Duration::from_secs(CONNECT_TIMEOUT_SECS);\n    tokio::select! {\n        _ = cancel.cancelled() => {\n            anyhow::bail!(\"Connection cancelled\");\n        }\n        result = tokio::time::timeout(connect_timeout, async {\n            if let Some(proxy_url) = proxy_url {\n                // Proxy path: open TCP to proxy, send CONNECT, then WS handshake.\n                let target_host = req.uri().host()\n                    .ok_or_else(|| anyhow::anyhow!(\"WebSocket URL has no host\"))?;\n                let target_port = req.uri().port_u16().unwrap_or(443);\n                let tunneled_stream = proxy::connect_via_proxy(\n                    proxy_url,\n                    target_host,\n                    target_port,\n                ).await?;\n                // Perform the WebSocket handshake over the tunneled stream.\n                let (ws, resp) = tokio_tungstenite::client_async(req, tunneled_stream)\n                    .await\n                    .map_err(|e| anyhow::Error::from(e).context(\"WebSocket handshake via proxy failed\"))?;\n                Ok((ws, resp))\n            } else {\n                // The default connector never sees the shared trust config.\n                let connector =\n                    tokio_tungstenite::Connector::Rustls(xai_grok_extra_ca::rustls_client_config());\n                connect_async_tls_with_config(req, None, false, Some(connector))\n                    .await\n                    .map_err(|e| anyhow::Error::from(e).context(\"WebSocket connection failed\"))","sourceCodeStart":403,"sourceCodeEnd":439,"githubUrl":"https://github.com/xai-org/grok-build/blob/bc7f02eddd3d84085849dc19ed216f11c23b0571/crates/codegen/xai-grok-shell/src/agent/relay.rs#L403-L439","documentation":"connect_to_relay builds a WebSocket connection; on the proxy path it needs the target host from the request URI to issue an HTTP CONNECT. If the WS URI has no host component (e.g. a relative or scheme-only URL) this error is thrown.","triggerScenarios":"Calling run_relay_loop with a relay WS URL like 'wss:/relay' (single slash), 'ws://' with empty authority, or a malformed/empty relay endpoint configured.","commonSituations":"Config typo dropping a slash in ws:// or wss:// URL, env var with trailing whitespace stripped of host, or relay URL constructed by string concatenation losing the authority.","solutions":["Fix the relay URL so it is an absolute URI with a host, e.g. wss://relay.example.com/ws","Validate the URL with url::Url::parse and check host_str().is_some() before running the loop","If built dynamically, use url::Url::builder instead of string concatenation"],"exampleFix":"// before\nlet relay = \"wss:/relay.example.com\";\n// after\nlet relay = \"wss://relay.example.com/ws\";","handlingStrategy":"validation","validationCode":"fn has_host(u: &str) -> bool {\n    url::Url::parse(u).ok()\n        .and_then(|u| u.host_str().map(|h| !h.is_empty()))\n        .unwrap_or(false)\n}\n// assert!(has_host(&relay_url));","typeGuard":null,"tryCatchPattern":"match result {\n    Err(e) if e.to_string().contains(\"WebSocket URL has no host\") => {\n        eprintln!(\"relay URL malformed, must be absolute ws:// or wss:// with host\");\n    }\n    other => other?,\n}","preventionTips":["Store relay endpoints as absolute ws:// or wss:// URLs","Parse and validate relay URLs once at startup","Build URLs with url::Url instead of string concatenation"],"tags":["websocket","proxy","url"],"backgroundTag":"websocket-url-has-no-host","analyzedSha":"bc7f02eddd3d84085849dc19ed216f11c23b0571","analyzedAt":"2026-08-31T04:59:42.031Z","schemaVersion":2},"datasetVersion":"2026-08-31T09:17:48.483Z"}