gitbutlerapp/gitbutler · error

Failed to resolve bin dir path

Error message

Failed to resolve bin dir path

What it means

install_app computes the user's binary install path via but_binary_path(home_dir) and then calls .parent() to obtain the bin directory to create. If the computed path has no parent (e.g. the path is a bare root/empty component), it throws "Failed to resolve bin dir path". This is an internal invariant violation: but_binary_path normally returns a nested path like <home>/bin/but, which always has a parent.

Solutions

  1. Check that the installer's home_dir / HOME is set to a normal absolute user home path (e.g. /home/alice), not "/" or empty
  2. Re-run the installer without custom home-dir overrides
  3. If it persists, inspect but_binary_path in but-installer to confirm it appends a subdirectory (bin) to home_dir

Example fix

// before
let bin_dir = install_bin_path.parent().ok_or_else(|| anyhow!("Failed to resolve bin dir path"))?;
// after
let bin_dir = install_bin_path.parent().unwrap_or_else(|| Path::new(home_dir)); // fall back to home dir
Defensive patterns

Strategy: validation

Validate before calling

let install_bin_path = but_binary_path(&home_dir);
if install_bin_path.parent().is_none() {
    return Err("home_dir does not yield a nested bin path".into());
}

Prevention

When it happens

Trigger: install_app is invoked with a home_dir for which but_binary_path returns a path without a parent component — practically only when the path resolution degenerates to a root or empty path.

Common situations: Running the installer with a misconfigured or empty home directory override (e.g. home_dir set to "/" or an empty string), or future changes to but_binary_path that return a non-nested path.

Understand the failure class

Background: "This is a bug, please report it": internal invariant violations, unreachable panics, and SNH errors explained — this error's family across 47 libraries.

Related errors


AI-assisted analysis of gitbutlerapp/gitbutler@58e5313667 (2026-09-18). Data as JSON: /api/errors/bf600211671d16df. Report an issue: GitHub.

Appendix: source

Thrown at crates/but-installer/src/install_linux.rs:67

    download_file(&download_url, &tmp_filepath)?;
    info("Download completed successfully");

    let signature_b64 = download_to_string(&signature_url).with_context(|| {
        anyhow!("Failed to get signature for but, requested version may be too old")
    })?;
    verify_signature(&tmp_filepath, &signature_b64, temp_dir.path())?;

    // Install the app bundle
    install_app(&tmp_filepath, &config.home_dir, channel)?;

    Ok(())
}

fn install_app(but_path: &Path, home_dir: &Path, channel: Option<Channel>) -> Result<()> {
    let install_bin_path = but_binary_path(home_dir);
    let bin_dir = install_bin_path
        .parent()
        .ok_or_else(|| anyhow!("Failed to resolve bin dir path"))?;
    fs::create_dir_all(bin_dir)?;

    let but_backup = if install_bin_path.is_file() {
        let suffix: u64 = std::time::SystemTime::now()
            .duration_since(std::time::UNIX_EPOCH)
            .map(|d| d.as_nanos() as u64 ^ std::process::id() as u64)
            .unwrap_or(std::process::id() as u64);
        let mut backup_name = install_bin_path.as_os_str().to_owned();
        backup_name.push(format!(".{suffix:x}"));
        let but_backup = std::path::PathBuf::from(backup_name);

        info(&format!(
            "Found existing install at {}, backing up to {}",
            install_bin_path.to_string_lossy(),
            but_backup.to_string_lossy()
        ));

        fs::rename(&install_bin_path, &but_backup)?;

View on GitHub (pinned to 58e5313667)