{"record":{"id":"5705682990013f27","repo":"gitbutlerapp/gitbutler","slug":"failed-to-extract-filename-from-download-url","errorCode":null,"errorMessage":"Failed to extract filename from download URL","messagePattern":"Failed to extract filename from download URL","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"crates/but-installer/src/install_macos.rs","lineNumber":48,"sourceCode":"    let download_url = platform_info.url.as_deref().ok_or_else(|| {\n        anyhow::anyhow!(\n            \"No download URL for platform {} in release {}\",\n            config.platform,\n            release.version\n        )\n    })?;\n\n    validate_download_url(download_url)?;\n    info(&format!(\"Download URL: {download_url}\"));\n\n    let temp_dir = tempfile::Builder::new()\n        .prefix(\"gitbutler-install.\")\n        .tempdir()?;\n\n    let filename = download_url\n        .split('/')\n        .next_back()\n        .ok_or_else(|| anyhow::anyhow!(\"Failed to extract filename from download URL\"))?;\n    let tarball_path = temp_dir.path().join(filename);\n\n    info(&format!(\"Downloading GitButler {}...\", release.version));\n    download_file(download_url, &tarball_path)?;\n\n    validate_tarball(&tarball_path)?;\n    success(\"Download completed successfully\");\n\n    verify_signature(&tarball_path, &platform_info.signature, temp_dir.path())?;\n\n    info(\"Extracting archive...\");\n    let app_dir = extract_tarball(&tarball_path, temp_dir.path())?;\n    success(\"Archive extracted successfully\");\n\n    verify_app_structure(&app_dir)?;\n\n    install_app(&app_dir, &config.home_dir, channel)?;\n","sourceCodeStart":30,"sourceCodeEnd":66,"githubUrl":"https://github.com/gitbutlerapp/gitbutler/blob/caf1f223d3cfb94488c9198ad34487c6006c648f/crates/but-installer/src/install_macos.rs#L30-L66","documentation":"Thrown in install_macos.rs when download_url.split('/').next_back() is None while deriving the tarball filename. In practice str::split always yields at least one element, so this arm is a defensive guard that is only reachable with a degenerate empty URL; for any real URL the last path segment becomes the filename.","triggerScenarios":"An empty or whitespace download_url reaching this code (which in practice cannot happen because validate_download_url runs first and requires https:// with a host); purely defensive code triggered by future refactors that bypass validation.","commonSituations":"Essentially unreachable in shipped code — if you see it, a code path skipped validate_download_url or passed a non-URL string; treat it as a bug report against the caller, not an environment problem.","solutions":["Confirm validate_download_url(download_url) ran before this point; if not, that is the bug to fix.","Log the actual download_url value to find which caller passed a degenerate string.","Update the but-installer crate — a refactor may have relaxed the earlier validation."],"exampleFix":null,"handlingStrategy":"validation","validationCode":"// Ensure the URL is non-degenerate before extracting a filename\nfn url_has_filename(url: &str) -> bool {\n    url::Url::parse(url)\n        .ok()\n        .and_then(|u| u.path_segments().and_then(Iterator::last))\n        .map(|s| !s.is_empty())\n        .unwrap_or(false)\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Always run validate_download_url before processing release URLs.","In custom callers, never pass empty or scheme-only strings where a file URL is expected."],"tags":["installer","macos","url-parsing","defensive-code"],"backgroundTag":"url-parsing-failed","analyzedSha":"caf1f223d3cfb94488c9198ad34487c6006c648f","analyzedAt":"2026-08-20T07:55:40.983Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}