gitbutlerapp/gitbutler · error

No host in {} URL

Error message

No host in {} URL

What it means

Thrown by validate_gitbutler_url in crates/but-installer/src/release.rs when a URL parsed successfully with url::Url::parse but has no host component (host_str() == None). This happens for URLs like "https:///path" or scheme-only forms where no authority follows the scheme; the same function also enforces HTTPS and the trusted gitbutler.com host allowlist used for both API and download URLs.

Source

Thrown at crates/but-installer/src/release.rs:112

/// Common URL validation logic for GitButler domains.
///
/// Validates HTTPS protocol, parses URL, and checks the host against a predicate.
fn validate_gitbutler_url(
    url: &str,
    url_type: &str,
    is_host_valid: impl Fn(&str) -> bool,
) -> Result<()> {
    // Only allow HTTPS URLs
    if !url.starts_with("https://") {
        bail!("{url_type} must use HTTPS: {url}");
    }

    // Extract host from URL
    let url_parsed =
        url::Url::parse(url).with_context(|| format!("Invalid {} URL", url_type.to_lowercase()))?;
    let host = url_parsed
        .host_str()
        .ok_or_else(|| anyhow!("No host in {} URL", url_type.to_lowercase()))?;

    // Validate host using the provided predicate
    if !is_host_valid(host) {
        bail!("{url_type} is not from a trusted GitButler domain: {url}");
    }

    Ok(())
}

/// Validates that an API URL is from the trusted API domain.
///
/// API endpoints should only be served from app.gitbutler.com to prevent
/// redirecting API requests to other subdomains.
pub(crate) fn validate_api_url(url: &str) -> Result<()> {
    validate_gitbutler_url(url, "API URL", |host| host == "app.gitbutler.com")
}

/// Validates that a download URL is from a trusted GitButler domain.

View on GitHub (pinned to caf1f223d3)

Solutions

  1. Fix the URL to include a host, e.g. https://app.gitbutler.com/releases.
  2. Check where the URL came from — env overrides, config files, or code that built it — and fix the empty-host bug at the source.
  3. If you need a custom endpoint, host it on a *.gitbutler.com domain or patch the validator; other hosts are rejected by design.

Example fix

// before
let base = format!("https://{}/releases", host_maybe_empty);

// after
let host = std::env::var("GB_API_HOST").unwrap_or_else(|_| "app.gitbutler.com".into());
assert!(!host.is_empty(), "API host must not be empty");
let base = format!("https://{host}/releases");
Defensive patterns

Strategy: validation

Validate before calling

// Before passing any URL into the installer's validators
fn url_has_https_host(url: &str) -> bool {
    url::Url::parse(url).ok().and_then(|u| u.host_str().map(|h| u.scheme() == "https" && !h.is_empty())).unwrap_or(false)
}

Type guard

fn is_valid_gitbutler_url(url: &str) -> bool {
    match url::Url::parse(url) {
        Ok(u) => matches!(u.host_str(), Some(h) if h == "gitbutler.com" || h.ends_with(".gitbutler.com")),
        Err(_) => false,
    }
}

Prevention

When it happens

Trigger: Passing an API or download URL that starts with https:// but lacks a host ("https:///releases"); a malformed custom endpoint or overriden URL reaching validate_api_url/validate_download_url; URL construction bugs that drop the host segment.

Common situations: A typo'd or template-substitution-broken URL (empty host variable) passed to the installer; env or config overrides that produce scheme-only URLs; test fixtures with hand-written malformed URLs.

Related errors


AI-assisted analysis of gitbutlerapp/gitbutler@caf1f223d3 (2026-08-20). Data as JSON: /api/errors/d845cbda48c23bbf. Report an issue: GitHub.