gitbutlerapp/gitbutler · error

No .app bundle found in extracted archive

Error message

No .app bundle found in extracted archive

What it means

Thrown by extract_tarball in install_macos.rs after unpacking the release tarball: it scans the extraction directory for a directory whose extension is "app" and found none. The archive downloaded and extracted fine, but its layout does not contain the expected top-level .app bundle that verify_app_structure and install_app require.

Source

Thrown at crates/but-installer/src/install_macos.rs:333

    let file = File::open(tarball)?;
    let decoder = GzDecoder::new(file);
    let mut archive = Archive::new(decoder);
    archive
        .unpack(dest_dir)
        .context("Failed to extract archive")?;

    // Find the extracted .app bundle
    let mut app_dir = None;
    for entry in fs::read_dir(dest_dir)? {
        let entry = entry?;
        let path = entry.path();
        if path.is_dir() && path.extension().and_then(|s| s.to_str()) == Some("app") {
            app_dir = Some(path);
            break;
        }
    }

    app_dir.ok_or_else(|| anyhow!("No .app bundle found in extracted archive"))
}

pub(crate) fn verify_app_structure(app_dir: &Path) -> Result<()> {
    let binaries_dir = app_dir.join("Contents/MacOS");
    if !binaries_dir.is_dir() {
        bail!(
            "Extracted app bundle does not contain expected directory structure (Contents/MacOS)"
        );
    }

    let required_binaries = ["gitbutler-git-askpass", "gitbutler-tauri"];

    for binary in &required_binaries {
        let binary_path = binaries_dir.join(binary);
        if !binary_path.exists() {
            bail!("Missing required binary: {binary}");
        }
    }

View on GitHub (pinned to caf1f223d3)

Solutions

  1. Manually download and inspect the tarball (tar -tf <tarball>) to see what it actually contains at the top level.
  2. Update the but-installer/but CLI to a version matching the release format you are installing.
  3. Re-download the archive to rule out corruption, and confirm verify_signature and validate_tarball passed on the same file.
  4. If the official archive genuinely lacks the bundle, report it to GitButler with the release version.
Defensive patterns

Strategy: try-catch

Validate before calling

// After extraction, check for the expected bundle before the installer does
fn find_app_bundle(dest: &std::path::Path) -> Option<std::path::PathBuf> {
    std::fs::read_dir(dest).ok()?.flatten().map(|e| e.path()).find(|p| {
        p.is_dir() && p.extension().and_then(|s| s.to_str()) == Some("app")
    })
}

Try / catch

match extract_tarball(&tarball_path, temp_dir.path()) {
    Ok(app_dir) => { /* verify + install */ },
    Err(e) if e.to_string().contains("No .app bundle") => {
        // re-download or switch to a release with the expected layout
    },
    Err(e) => return Err(e),
}

Prevention

When it happens

Trigger: A release archive with a changed layout (bundle nested deeper, different naming, or zipped rather than a plain tarball); a tampered or corrupted archive that extracted into unexpected paths; a proxy serving an HTML error page that happened to decompress as an empty tar.

Common situations: A new release packaging change the installer version does not understand; installing an old installer against a future release format; CDN or proxy interference mangling the artifact.

Related errors


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