{"record":{"id":"7ba03327c7dfab11","repo":"BloopAI/vibe-kanban","slug":"unexpected-base-url-scheme-base-url","errorCode":null,"errorMessage":"Unexpected base URL scheme: {base_url}","messagePattern":"Unexpected base URL scheme: (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/server/src/runtime/relay_registration.rs","lineNumber":140,"sourceCode":"/// Start the relay client transport.\nasync fn start_relay(\n    params: &RelayParams,\n    shutdown: tokio_util::sync::CancellationToken,\n) -> anyhow::Result<()> {\n    let base_url = params.relay_base.trim_end_matches('/');\n\n    let encoded_name = url::form_urlencoded::Serializer::new(String::new())\n        .append_pair(\"machine_id\", &params.machine_id)\n        .append_pair(\"name\", &params.host_nickname)\n        .append_pair(\"agent_version\", env!(\"CARGO_PKG_VERSION\"))\n        .finish();\n\n    let ws_url = if let Some(rest) = base_url.strip_prefix(\"https://\") {\n        format!(\"wss://{rest}/v1/relay/connect?{encoded_name}\")\n    } else if let Some(rest) = base_url.strip_prefix(\"http://\") {\n        format!(\"ws://{rest}/v1/relay/connect?{encoded_name}\")\n    } else {\n        anyhow::bail!(\"Unexpected base URL scheme: {base_url}\");\n    };\n\n    let access_token = params\n        .remote_client\n        .access_token()\n        .await\n        .context(\"Failed to get access token for relay\")?;\n\n    tracing::debug!(%ws_url, \"Connecting relay control channel\");\n\n    start_relay_client(RelayClientConfig {\n        ws_url,\n        bearer_token: access_token,\n        local_addr: params.server_addr,\n        shutdown,\n    })\n    .await\n}","sourceCodeStart":122,"sourceCodeEnd":158,"githubUrl":"https://github.com/BloopAI/vibe-kanban/blob/4deb7eca8f381f7cbc1f9d15515a9ab8f8009053/crates/server/src/runtime/relay_registration.rs#L122-L158","documentation":"start_relay builds a WebSocket URL for the relay connection by rewriting the remote server's base URL scheme: https:// becomes wss:// and http:// becomes ws://. If the configured base URL uses any other (or no) scheme, the function bails with this message because it cannot derive a valid WebSocket endpoint. It is a configuration-validation guard against malformed REMOTE_BASE_URL-style inputs.","triggerScenarios":"Calling spawn_relay (which calls start_relay) with a base_url that does not start with 'https://' or 'http://' — e.g. 'myserver.example.com', 'ftp://host', 'wss://host' already supplied, or an empty string.","commonSituations":"Developers set the remote base URL env var without a scheme, copy a host from docs without 'https://', or pre-convert to wss:// themselves and pass it in, not realizing the relay adds the scheme.","solutions":["Prefix the base URL with 'https://' (or 'http://' for local dev) before passing it to spawn_relay/start_relay.","Normalize the value at config load: if it has no scheme, default to https://.","Strip any user-supplied ws:// or wss:// scheme and store only the http(s) origin, letting start_relay do the conversion.","Log/echo the offending base_url value from the error message and fix the environment variable or config file that supplies it."],"exampleFix":"// before\nlet base_url = \"myserver.example.com\";\nspawn_relay(base_url, ...).await?;\n// after\nlet base_url = if base_url.starts_with(\"http\") { base_url } else { format!(\"https://{base_url}\") };\nspawn_relay(&base_url, ...).await?;","handlingStrategy":"validation","validationCode":"fn ensure_http_base_url(base_url: &str) -> Result<(), String> {\n    if base_url.starts_with(\"https://\") || base_url.starts_with(\"http://\") {\n        Ok(())\n    } else {\n        Err(format!(\"base URL must start with http:// or https://, got: {base_url}\"))\n    }\n}","typeGuard":"fn is_http_base_url(base_url: &str) -> bool {\n    base_url.starts_with(\"https://\") || base_url.starts_with(\"http://\")\n}","tryCatchPattern":"match start_relay(&base_url, params).await {\n    Ok(()) => {},\n    Err(e) if e.to_string().starts_with(\"Unexpected base URL scheme\") => {\n        eprintln!(\"Fix base URL (must be http/https): {base_url}\");\n    }\n    Err(e) => return Err(e),\n}","preventionTips":["Always store the remote base URL with an explicit http(s) scheme in env/config.","Normalize user input at config load time by prepending https:// when no scheme is present.","Reject ws:// or wss:// values early — the relay derives the scheme itself.","Add a startup config check that validates the base URL before any relay operations."],"tags":["configuration","websocket","url-validation"],"backgroundTag":"invalid-base-url-scheme","analyzedSha":"4deb7eca8f381f7cbc1f9d15515a9ab8f8009053","analyzedAt":"2026-08-29T09:24:13.446Z","schemaVersion":2},"datasetVersion":"2026-08-29T12:17:43.993Z"}