gitbutlerapp/gitbutler · error
Failed to construct but cli URL
Error message
Failed to construct but cli URL
What it means
On Linux the CLI binary and its signature are found 'by convention' by stripping the filename from the AppImage URL. This error fires when rsplit_once('/') fails — i.e. the AppImage URL contains no '/' at all — so the base URL for the `but` CLI cannot be derived.
Solutions
- Check the release metadata: platform_info.url should be a full https URL with slashes
- Report/fix the release publishing pipeline if the URL is malformed server-side
- As a caller, validate the URL shape before invoking the installer
Example fix
// before
let base = url.rsplit_once('/').map(|(b, _)| b.to_string()).ok_or_else(...)?;
// after
anyhow::ensure!(url.contains("/"), "invalid asset url: {url}");
let base = url.rsplit_once('/').unwrap().0.to_string(); Defensive patterns
Strategy: validation
Validate before calling
fn plausible_asset_url(u: &str) -> bool {
u.starts_with("https://") && u.contains('/') && u.rsplit('/').next().map_or(false, |f| !f.is_empty())
} Try / catch
if let Err(e) = download_and_install_app(...) {
if e.to_string().contains("Failed to construct but cli URL") {
eprintln!("Release metadata has a malformed asset URL — report to publishers");
}
return Err(e.into());
} Prevention
- Validate asset URLs (scheme + path) before install
- Treat slash-less URLs as broken release metadata and skip that release
- Add a publishing-pipeline check that every asset URL is absolute
When it happens
Trigger: download_and_install_app on Linux when platform_info.url is a bare filename or otherwise slash-less string, making rsplit_once('/') return None.
Common situations: A malformed release asset URL stored on the server (e.g. a relative path without slashes); corrupted release metadata from a broken publisher pipeline.
Understand the failure class
Background: "Invalid URL" errors: why new URL(), URI.parse, and reqwest::Url reject your string — missing scheme, whitespace, and bad path format — this error's family across 39 libraries.
Related errors
- Attempting to restore backup
- Failed to get signature for but, requested version may be…
- Failed to parse URL
- Failed to resolve bin dir path
- Final installation verification failed
AI-assisted analysis of gitbutlerapp/gitbutler@58e5313667 (2026-09-18).
Data as JSON: /api/errors/98c6d9e1577389ba.
Report an issue: GitHub.
Appendix: source
Thrown at crates/but-installer/src/install_linux.rs:35
platform_info: &PlatformInfo,
release: &Release,
channel: Option<Channel>,
) -> Result<()> {
let appimage_download_url = platform_info.url.as_deref().ok_or_else(|| {
anyhow::anyhow!(
"No download URL for platform {} in release {}",
config.platform,
release.version
)
})?;
// Note: For now we find the CLI and signature by convention on Linux, but we should update the
// API (or create a new one) to contain this information.
let filename = "but";
let base_download_url = appimage_download_url
.rsplit_once('/')
.map(|(base, _)| base.to_string())
.ok_or_else(|| anyhow::anyhow!("Failed to construct but cli URL"))?;
let download_url = format!("{base_download_url}/{filename}");
let signature_url = format!("{download_url}.sig");
validate_download_url(&signature_url)?;
validate_download_url(&download_url)?;
info(&format!("Download URL: {download_url}"));
let temp_dir = tempfile::Builder::new()
.prefix("gitbutler-install.")
.tempdir()?;
let tmp_filepath = temp_dir.path().join(filename);
info(&format!("Downloading GitButler {}...", release.version));
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")View on GitHub (pinned to 58e5313667)