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
- Fix the URL to include a host, e.g. https://app.gitbutler.com/releases.
- Check where the URL came from — env overrides, config files, or code that built it — and fix the empty-host bug at the source.
- 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
- Always build API/download URLs from a host constant plus path, never string templates with possibly-empty host variables.
- Assert !host.is_empty() before formatting URLs.
- Remember the allowlist only accepts *.gitbutler.com hosts over HTTPS — design custom endpoints accordingly.
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
- Effective URL is missing
- Too many arguments. Usage: but-installer [version|nightly] o
- unsupported OS or architecture: {os} {arch}
- JSON output is not supported for 'but update install'. The
- Failed to determine home directory
AI-assisted analysis of gitbutlerapp/gitbutler@caf1f223d3 (2026-08-20).
Data as JSON: /api/errors/d845cbda48c23bbf.
Report an issue: GitHub.