gitbutlerapp/gitbutler · error
Downloaded file does not appear to be a valid gzip archive
Error message
Downloaded file does not appear to be a valid gzip archive
What it means
After the emptiness check, validate_tarball reads the file's first bytes and requires the gzip magic bytes 1f 8b. This error means the downloaded file exists but is not a gzip archive, so extraction would fail and the installer stops here instead.
Solutions
- Check the downloaded file with `file` / `xxd path | head` to see what it actually is
- Verify the download URL points at the correct .tar.gz asset and not an HTML error page
- Delete the file and re-download; check for redirects to untrusted/intermediate pages
- Confirm the release asset on the server is a valid gzip archive
Example fix
// before
let resp = client.get(url).send()?;
fs::write(path, resp.bytes()?);
// after
let resp = client.get(url).send()?;
anyhow::ensure!(resp.status() == 200, "HTTP {} for {url}", resp.status());
let ct = resp.headers().get("content-type").and_then(|v| v.to_str().ok()).unwrap_or("");
anyhow::ensure!(!ct.contains("text/html"), "unexpected content-type {ct}");
fs::write(path, resp.bytes()?)?; Defensive patterns
Strategy: validation
Validate before calling
fn has_gzip_magic(path: &std::path::Path) -> std::io::Result<bool> {
use std::io::Read;
let mut f = std::fs::File::open(path)?;
let mut b = [0u8; 2];
f.read_exact(&mut b)?;
Ok(b == [0x1f, 0x8b])
} Try / catch
match result {
Err(e) if e.to_string().contains("valid gzip archive") => {
// inspect file content, re-download from correct asset URL
}
other => other?,
} Prevention
- Validate Content-Type is application/gzip or application/x-tar+gzip
- Never save a 200 HTML error page as the asset
- Use checksums published alongside the release
- Verify magic bytes before extraction in your own pipeline
When it happens
Trigger: The downloaded asset is not gzip-compressed: HTML/error page saved instead of the tarball, plain tar (uncompressed), zip, or binary corruption altering the first two bytes.
Common situations: Wrong asset URL (e.g. API returns an error page with 200), CDN serving an error/captcha page, release asset replaced with uncompressed tar, redirect to a login page.
Understand the failure class
Background: "Invalid ... format", "must be in format X", "does not look like a ..." — invalid argument format errors across CLI tools and libraries — this error's family across 17 libraries.
Related errors
- Downloaded file is empty
- CLI destination must be absolute
- CLI source ' ' must be an executable file
- CLI source path must be absolute
- Extracted app bundle does not contain expected directory…
AI-assisted analysis of gitbutlerapp/gitbutler@58e5313667 (2026-09-18).
Data as JSON: /api/errors/cb747e0bb61d4f01.
Report an issue: GitHub.
Appendix: source
Thrown at crates/but-installer/src/install_macos.rs:365
let binary_path = binaries_dir.join(binary);
if !binary_path.exists() {
bail!("Missing required binary: {binary}");
}
}
Ok(())
}
fn validate_tarball(path: &Path) -> Result<()> {
// Check if file is not empty
let metadata = fs::metadata(path).context("Failed to read tarball metadata")?;
if metadata.len() == 0 {
bail!("Downloaded file is empty");
}
// Check for gzip magic bytes (1f 8b)
if !is_gzip_file(path)? {
bail!("Downloaded file does not appear to be a valid gzip archive");
}
Ok(())
}
/// Check if a file has valid gzip magic bytes
fn is_gzip_file(path: &Path) -> Result<bool> {
let mut file = File::open(path).context("Failed to open file")?;
let mut magic_bytes = [0u8; 2];
// If we can't read 2 bytes, it's definitely not a valid gzip file
match file.read_exact(&mut magic_bytes) {
Ok(_) => Ok(magic_bytes == [0x1f, 0x8b]),
Err(_) => Ok(false),
}
}
/// Recursively remove the macOS quarantine attribute from a directoryView on GitHub (pinned to 58e5313667)