Hmbown/CodeWhale · error

custom provider base URL is required

Error message

custom provider base URL is required

What it means

normalize_custom_provider_base_url rejects an empty base URL after trimming and trailing-slash removal; persist_custom_provider requires every custom provider to carry a non-empty endpoint before it is written to config.

Solutions

  1. Provide the provider's API endpoint (e.g. "https://api.example.com/v1") before persisting.
  2. Require the URL field in the caller/UI before submit.

Example fix

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

Strategy: validation

Validate before calling

if url.trim().is_empty() { return Err(anyhow::anyhow!("base URL required")); }

Type guard

fn has_base_url(raw: &str) -> bool { !raw.trim().trim_end_matches('/').is_empty() }

Try / catch

match persist_custom_provider(path, name, &url, ...) {
    Err(e) if e.to_string().contains("base URL is required") => show_form_error("URL required"),
    other => other,
}

Prevention

When it happens

Trigger: Calling persist_custom_provider with an empty or whitespace-only base_url string.

Common situations: A form submitted before the URL field was filled; an env var or config value that expanded to empty.

Understand the failure class

Background: "must not be empty", "cannot be empty" — required-field validation errors across open-source libraries — this error's family across 41 libraries.

Related errors


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

Appendix: source

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

    }
    if crate::config::ApiProvider::parse(value).is_some() {
        bail!("custom provider name must not shadow a built-in provider");
    }
    if !value
        .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> {

View on GitHub (pinned to 73e0f67d83)