gitbutlerapp/gitbutler · error

Download failed with HTTP status: {response_code}

Error message

Download failed with HTTP status: {response_code}

What it means

download_file() checks the final HTTP status after transferring the CLI tarball and bails for any code that is neither 200 nor 403. Unlike the dedicated 403 branch, this indicates a transport or server problem (404 stale URL, 429 rate limit, 5xx) rather than a missing artifact. Nothing is installed and the previous installation is untouched.

Source

Thrown at crates/but-installer/src/download.rs:129

            return Err(io_err).context("Failed to write downloaded data");
        }

        // If perform failed for other reasons, propagate that error
        perform_result.with_context(|| format!("Failed to download from {url}"))?;
    }

    // Clear progress line
    crate::ui::println_empty();

    let response_code = easy
        .response_code()
        .context("Failed to get response code")?;
    if response_code == 403 {
        bail!(
            "Download failed, the download artifact could not be found. Most likely, the but CLI has not been published for the requested version."
        )
    } else if response_code != 200 {
        bail!("Download failed with HTTP status: {response_code}");
    }

    // Validate the effective URL after following redirects
    // This protects against malicious redirects to untrusted domains or insecure protocols
    let effective_url = easy
        .effective_url()
        .context("Failed to get effective URL")?
        .ok_or_else(|| anyhow!("Effective URL is missing"))?;

    crate::release::validate_download_url(effective_url)
        .with_context(|| format!("Download was redirected to an untrusted URL: {effective_url}"))?;

    Ok(())
}

View on GitHub (pinned to caf1f223d3)

Solutions

  1. Retry with exponential backoff - 5xx and 429 are transient
  2. curl -v the URL to see the exact status; a 404 means metadata/CDN skew, retry later or use another version
  3. Reduce install frequency or cache artifacts in CI to avoid 429s
  4. Check GitButler status pages for an ongoing release incident
Defensive patterns

Strategy: retry

Validate before calling

// HEAD-check the tarball URL before launching the installer
let resp = client.head(&tarball_url).send()?;
if !resp.status().is_success() {
    anyhow::bail!("artifact not ready: HTTP {}", resp.status());
}

Try / catch

let mut attempt = 0u32;
loop {
    match run_install() {
        Err(e) if e.to_string().contains("Download failed with HTTP status") && attempt < 3 => {
            attempt += 1;
            std::thread::sleep(std::time::Duration::from_millis(500 * 2u64.pow(attempt)));
        }
        Err(e) if e.to_string().contains("artifact could not be found") => {
            break fall_back_to_latest_release(); // not retryable
        }
        other => break other,
    }
}

Prevention

When it happens

Trigger: 404 when the platform URL in the release metadata is stale or wrong; 5xx during a CDN incident; 429 from repeated automated installs; proxies or middleboxes returning 502/504 for releases.gitbutler.com.

Common situations: CI pipelines hammering the download host and hitting rate limits; CDN partial outages; metadata/CDN skew right after a release where the JSON updates before objects propagate.

Related errors


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