{"record":{"id":"bf600211671d16df","repo":"gitbutlerapp/gitbutler","slug":"failed-to-resolve-bin-dir-path","errorCode":null,"errorMessage":"Failed to resolve bin dir path","messagePattern":"Failed to resolve bin dir path","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/but-installer/src/install_linux.rs","lineNumber":67,"sourceCode":"    download_file(&download_url, &tmp_filepath)?;\n    info(\"Download completed successfully\");\n\n    let signature_b64 = download_to_string(&signature_url).with_context(|| {\n        anyhow!(\"Failed to get signature for but, requested version may be too old\")\n    })?;\n    verify_signature(&tmp_filepath, &signature_b64, temp_dir.path())?;\n\n    // Install the app bundle\n    install_app(&tmp_filepath, &config.home_dir, channel)?;\n\n    Ok(())\n}\n\nfn install_app(but_path: &Path, home_dir: &Path, channel: Option<Channel>) -> Result<()> {\n    let install_bin_path = but_binary_path(home_dir);\n    let bin_dir = install_bin_path\n        .parent()\n        .ok_or_else(|| anyhow!(\"Failed to resolve bin dir path\"))?;\n    fs::create_dir_all(bin_dir)?;\n\n    let but_backup = if install_bin_path.is_file() {\n        let suffix: u64 = std::time::SystemTime::now()\n            .duration_since(std::time::UNIX_EPOCH)\n            .map(|d| d.as_nanos() as u64 ^ std::process::id() as u64)\n            .unwrap_or(std::process::id() as u64);\n        let mut backup_name = install_bin_path.as_os_str().to_owned();\n        backup_name.push(format!(\".{suffix:x}\"));\n        let but_backup = std::path::PathBuf::from(backup_name);\n\n        info(&format!(\n            \"Found existing install at {}, backing up to {}\",\n            install_bin_path.to_string_lossy(),\n            but_backup.to_string_lossy()\n        ));\n\n        fs::rename(&install_bin_path, &but_backup)?;","sourceCodeStart":49,"sourceCodeEnd":85,"githubUrl":"https://github.com/gitbutlerapp/gitbutler/blob/58e5313667b857ef39a730e380af31816a7b1768/crates/but-installer/src/install_linux.rs#L49-L85","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Check that the installer's home_dir / HOME is set to a normal absolute user home path (e.g. /home/alice), not \"/\" or empty","Re-run the installer without custom home-dir overrides","If it persists, inspect but_binary_path in but-installer to confirm it appends a subdirectory (bin) to home_dir"],"exampleFix":"// before\nlet bin_dir = install_bin_path.parent().ok_or_else(|| anyhow!(\"Failed to resolve bin dir path\"))?;\n// after\nlet bin_dir = install_bin_path.parent().unwrap_or_else(|| Path::new(home_dir)); // fall back to home dir","handlingStrategy":"validation","validationCode":"let install_bin_path = but_binary_path(&home_dir);\nif install_bin_path.parent().is_none() {\n    return Err(\"home_dir does not yield a nested bin path\".into());\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Always pass an absolute, non-root home directory to the installer","Assert home_dir is not \"/\" or empty before install","Add a unit test for but_binary_path with edge-case home dirs"],"tags":["installer","path-resolution","linux"],"backgroundTag":"internal-invariant-violation","analyzedSha":"58e5313667b857ef39a730e380af31816a7b1768","analyzedAt":"2026-09-18T06:50:32.052Z","contentChangedAt":"2026-09-18T06:50:32.052Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}