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
- Manually download and inspect the tarball (tar -tf <tarball>) to see what it actually contains at the top level.
- Update the but-installer/but CLI to a version matching the release format you are installing.
- Re-download the archive to rule out corruption, and confirm verify_signature and validate_tarball passed on the same file.
- 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
- Keep the installer version in sync with the release format you install.
- Inspect tar -tf output when automating installs against new channels.
- Verify signature and tarball validation passed — they usually catch corrupted archives before this stage.
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
- No download URL for platform {} in release {}
- Failed to get app bundle name
- Extracted app bundle does not contain expected directory str
- Failed to extract filename from download URL
- Failed to move new installation into place - restoring backu
AI-assisted analysis of gitbutlerapp/gitbutler@caf1f223d3 (2026-08-20).
Data as JSON: /api/errors/4a3ae13565c5bb28.
Report an issue: GitHub.