Hmbown/CodeWhale · error

custom provider base URL must be an http(s) URL with a host

Error message

custom provider base URL must be an http(s) URL with a host

What it means

The base URL must parse with reqwest::Url and use the http or https scheme with a host. Anything else (missing scheme, ftp://, no host) bails so malformed endpoints never reach the config. A distinct message ("custom provider base URL is invalid: {err}") is returned when reqwest itself fails to parse; this bail covers a parseable URL with the wrong scheme or no host.

Solutions

  1. Prefix the value with https:// (or http://) if the scheme is missing.
  2. Verify the URL parses and has an http/https scheme plus a host before calling.
  3. Check the error text to distinguish parse failure from wrong-scheme/no-host.

Example fix

// before
persist_custom_provider(path, "my_gateway", "api.example.com/v1", ...)?;
// after
persist_custom_provider(path, "my_gateway", "https://api.example.com/v1", ...)?;
Defensive patterns

Strategy: validation

Validate before calling

use reqwest::Url;
let u = Url::parse(url.trim().trim_end_matches('/'))?;
if !matches!(u.scheme(), "http" | "https") || u.host_str().is_none() {
    return Err(anyhow::anyhow!("must be http(s) URL with host"));
}

Type guard

fn is_http_url(raw: &str) -> bool {
    reqwest::Url::parse(raw.trim().trim_end_matches('/'))
        .map(|u| matches!(u.scheme(), "http" | "https") && u.host_str().is_some())
        .unwrap_or(false)
}

Try / catch

match persist_custom_provider(path, name, &url, ...) {
    Err(e) if e.to_string().contains("http(s) URL") || e.to_string().contains("base URL is invalid") => show_url_error(&e),
    other => other,
}

Prevention

When it happens

Trigger: Calling persist_custom_provider with e.g. "api.example.com/v1" (no scheme), "ftp://host", or a URL with no host component.

Common situations: Users omitting the https:// prefix; pasting a socket path or an internal hostname without a scheme.

Understand the failure class

Background: "Invalid URL" / "URL cannot be empty": fix the malformed or missing URL behind request-construction failures — this error's family across 50 libraries.

Related errors


AI-assisted analysis of Hmbown/CodeWhale@73e0f67d83 (2026-09-22). Data as JSON: /api/errors/4dfb761d15348399. Report an issue: GitHub.

Appendix: source

Thrown at crates/tui/src/config_persistence.rs:717

        .chars()
        .all(|ch| ch.is_ascii_alphanumeric() || matches!(ch, '_' | '-'))
    {
        bail!("custom provider name may only use letters, numbers, '-' and '_'");
    }
    Ok(value.to_string())
}

fn normalize_custom_provider_base_url(raw: &str) -> anyhow::Result<String> {
    use anyhow::bail;

    let value = raw.trim().trim_end_matches('/');
    if value.is_empty() {
        bail!("custom provider base URL is required");
    }
    let parsed = reqwest::Url::parse(value)
        .map_err(|err| anyhow::anyhow!("custom provider base URL is invalid: {err}"))?;
    if !matches!(parsed.scheme(), "http" | "https") || parsed.host_str().is_none() {
        bail!("custom provider base URL must be an http(s) URL with a host");
    }
    Ok(value.to_string())
}

fn normalize_optional_custom_provider_field(raw: &str) -> Option<String> {
    let value = raw.trim();
    (!value.is_empty()).then(|| value.to_string())
}

pub(crate) fn persist_hotbar_bindings(
    config_path: Option<&Path>,
    bindings: &[codewhale_config::HotbarBindingToml],
) -> anyhow::Result<PathBuf> {
    let path = config_toml_path(config_path)?;
    mutate_config_document(&path, |doc| {
        let table = doc.as_table_mut();
        table.remove("hotbar");
        if bindings.is_empty() {

View on GitHub (pinned to 73e0f67d83)