gitbutlerapp/gitbutler · error

must be an HTTP or HTTPS URL

Error message

{field} must be an HTTP or HTTPS URL

What it means

Raised by validate_url when a URL-shaped config value parses but is not an http/https URL with a host (e.g. ftp://, file://, or a URL without host). The library requires base URLs to be valid HTTP(S) endpoints.

Solutions

  1. Prefix the value with http:// or https:// and include a host
  2. Verify with a quick parse: url::Url::parse(value) has scheme http/https and a host
  3. Use the provider's default endpoint if unsure

Example fix

// before
endpoint = "localhost:11434"
// after
endpoint = "http://localhost:11434"
Defensive patterns

Strategy: validation

Validate before calling

// TS
function isHttpUrl(v: string): boolean {
  try { const u = new URL(v); return u.protocol === 'http:' || u.protocol === 'https:' && !!u.hostname; }
  catch { return false; }
}

Type guard

const isHttpUrl = (v: unknown): v is string => {
  if (typeof v !== 'string') return false;
  try { const u = new URL(v); return (u.protocol === 'http:' || u.protocol === 'https:') && u.hostname !== ''; }
  catch { return false; }
};

Prevention

When it happens

Trigger: Providing a base URL like "localhost:8080" (no scheme), "ftp://host", or "https://" (no host) to validate or apply_openai_configuration/apply_lmstudio_configuration.

Common situations: Copy-pasting an endpoint without the scheme, pasting a socket path instead of a URL, trailing-scheme typos.

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 gitbutlerapp/gitbutler@58e5313667 (2026-09-18). Data as JSON: /api/errors/c1ccef5b5e1339c3. Report an issue: GitHub.

Appendix: source

Thrown at crates/but-llm/src/config.rs:409

}

fn config_value(config: &gix::config::File, key: &str) -> Option<String> {
    config.string(key).map(|value| value.to_string())
}

fn required<'a>(value: &'a str, field: &str) -> Result<&'a str> {
    let value = value.trim();
    if value.is_empty() {
        bail!("{field} is required")
    }
    Ok(value)
}

fn validate_url(value: &str, field: &str) -> Result<()> {
    let value = required(value, field)?;
    let url = url::Url::parse(value).with_context(|| format!("{field} must be a valid URL"))?;
    if !matches!(url.scheme(), "http" | "https") || url.host_str().is_none() {
        bail!("{field} must be an HTTP or HTTPS URL")
    }
    Ok(())
}

fn validate_ollama_endpoint(value: &str) -> Result<()> {
    let value = required(value, "Ollama endpoint")?;
    if value.contains("://") {
        bail!("Ollama endpoint must use host:port format")
    }
    let (host, port) = value
        .rsplit_once(':')
        .context("Ollama endpoint must use host:port format")?;
    if host.trim().is_empty() || port.parse::<u16>().is_err() {
        bail!("Ollama endpoint must use host:port format")
    }
    Ok(())
}

View on GitHub (pinned to 58e5313667)