gitbutlerapp/gitbutler · error

is not from a trusted GitButler domain

Error message

{url_type} is not from a trusted GitButler domain: {url}

What it means

After the HTTPS check, validate_gitbutler_url parses the URL and checks its host against a domain allowlist predicate (validate_api_url and validate_download_url each supply one). This error means the URL points at a host outside GitButler's trusted domains, blocking potential malicious redirects or misconfiguration.

Solutions

  1. Use the official GitButler API/download domains as configured by the installer
  2. Fix typos in a custom-configured host
  3. Investigate if a redirect changed the host unexpectedly (possible security issue)
  4. If you legitimately need another host, the allowlist predicates must be updated upstream

Example fix

null
Defensive patterns

Strategy: validation

Validate before calling

fn is_trusted_host(url: &str) -> bool {
    url::Url::parse(url).ok()
        .and_then(|u| u.host_str().map(|h| h.to_string()))
        .map(|h| h == "api.gitbutler.com" || h.ends_with(".gitbutler.com"))
        .unwrap_or(false)
}

Try / catch

match result {
    Err(e) if e.to_string().contains("trusted GitButler domain") => {
        // treat as potential security issue: log host, refuse custom mirrors
    }
    other => other?,
}

Prevention

When it happens

Trigger: A download/API URL whose hostname is not in the allowlist: custom mirror, attacker-controlled redirect target, typo'd domain (e.g. gitbutlr.com), or test/staging host.

Common situations: Open-redirect responses from the release API, DNS hijacking or proxy rewriting hosts, developer configuring an unofficial mirror, DNS rebinding during incident testing.

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/851647a63168ab7f. Report an issue: GitHub.

Appendix: source

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

    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.
///
/// This is more permissive than API validation, allowing downloads from:
/// - `gitbutler.com` (root domain)
/// - Any `*.gitbutler.com` subdomain (e.g., `releases.gitbutler.com`, `cdn.gitbutler.com`)

View on GitHub (pinned to 58e5313667)