BloopAI/vibe-kanban · error

Unexpected base URL scheme: {base_url}

Error message

Unexpected base URL scheme: {base_url}

What it means

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.

Source

Thrown at crates/server/src/runtime/relay_registration.rs:140

/// Start the relay client transport.
async fn start_relay(
    params: &RelayParams,
    shutdown: tokio_util::sync::CancellationToken,
) -> anyhow::Result<()> {
    let base_url = params.relay_base.trim_end_matches('/');

    let encoded_name = url::form_urlencoded::Serializer::new(String::new())
        .append_pair("machine_id", &params.machine_id)
        .append_pair("name", &params.host_nickname)
        .append_pair("agent_version", env!("CARGO_PKG_VERSION"))
        .finish();

    let ws_url = if let Some(rest) = base_url.strip_prefix("https://") {
        format!("wss://{rest}/v1/relay/connect?{encoded_name}")
    } else if let Some(rest) = base_url.strip_prefix("http://") {
        format!("ws://{rest}/v1/relay/connect?{encoded_name}")
    } else {
        anyhow::bail!("Unexpected base URL scheme: {base_url}");
    };

    let access_token = params
        .remote_client
        .access_token()
        .await
        .context("Failed to get access token for relay")?;

    tracing::debug!(%ws_url, "Connecting relay control channel");

    start_relay_client(RelayClientConfig {
        ws_url,
        bearer_token: access_token,
        local_addr: params.server_addr,
        shutdown,
    })
    .await
}

View on GitHub (pinned to 4deb7eca8f)

Solutions

  1. Prefix the base URL with 'https://' (or 'http://' for local dev) before passing it to spawn_relay/start_relay.
  2. Normalize the value at config load: if it has no scheme, default to https://.
  3. Strip any user-supplied ws:// or wss:// scheme and store only the http(s) origin, letting start_relay do the conversion.
  4. Log/echo the offending base_url value from the error message and fix the environment variable or config file that supplies it.

Example fix

// before
let base_url = "myserver.example.com";
spawn_relay(base_url, ...).await?;
// after
let base_url = if base_url.starts_with("http") { base_url } else { format!("https://{base_url}") };
spawn_relay(&base_url, ...).await?;
Defensive patterns

Strategy: validation

Validate before calling

fn ensure_http_base_url(base_url: &str) -> Result<(), String> {
    if base_url.starts_with("https://") || base_url.starts_with("http://") {
        Ok(())
    } else {
        Err(format!("base URL must start with http:// or https://, got: {base_url}"))
    }
}

Type guard

fn is_http_base_url(base_url: &str) -> bool {
    base_url.starts_with("https://") || base_url.starts_with("http://")
}

Try / catch

match start_relay(&base_url, params).await {
    Ok(()) => {},
    Err(e) if e.to_string().starts_with("Unexpected base URL scheme") => {
        eprintln!("Fix base URL (must be http/https): {base_url}");
    }
    Err(e) => return Err(e),
}

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Related errors


AI-assisted analysis of BloopAI/vibe-kanban@4deb7eca8f (2026-08-29). Data as JSON: /api/errors/7ba03327c7dfab11. Report an issue: GitHub.