{"record":{"id":"2bc1c04a1b08b809","repo":"xai-org/grok-build","slug":"download-failed-http","errorCode":null,"errorMessage":"Download failed: HTTP {}","messagePattern":"Download failed: HTTP (.+?)","errorType":"http","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/codegen/xai-grok-update/src/auto_update.rs","lineNumber":1237,"sourceCode":"/// If the server provides a `Content-Length` header, a determinate bar is shown\n/// with bytes downloaded, total size, and ETA. Otherwise a spinner with a byte\n/// counter is used as a fallback.\n#[doc(hidden)]\npub async fn download_with_progress(url: &str, dest: &std::path::Path) -> Result<()> {\n    // Try parallel byte-range first. Falls through to single-connection on any\n    // failure (HEAD missing Content-Length, ranges rejected, partial-fetch error).\n    match try_parallel_download(url, dest, true).await {\n        Ok(()) => return Ok(()),\n        Err(e) => {\n            tracing::debug!(\"parallel download failed, falling back to single connection: {e}\")\n        }\n    }\n\n    let client = download_client()?;\n    let resp = client.get(url).send().await?;\n\n    if !resp.status().is_success() {\n        anyhow::bail!(\"Download failed: HTTP {}\", resp.status());\n    }\n\n    let total_size = resp.content_length();\n\n    let pb = if let Some(size) = total_size {\n        let pb = ProgressBar::new(size);\n        pb.set_style(\n            ProgressStyle::default_bar()\n                .template(\"  {bar:30.cyan/dim} {bytes}/{total_bytes} ({eta})\")\n                .unwrap()\n                .progress_chars(\"━╸─\"),\n        );\n        pb\n    } else {\n        let pb = ProgressBar::new_spinner();\n        pb.set_style(\n            ProgressStyle::default_spinner()\n                .template(\"  {spinner:.cyan} {bytes} downloaded\")","sourceCodeStart":1219,"sourceCodeEnd":1255,"githubUrl":"https://github.com/xai-org/grok-build/blob/bc7f02eddd3d84085849dc19ed216f11c23b0571/crates/codegen/xai-grok-update/src/auto_update.rs#L1219-L1255","documentation":"download_with_progress downloads an artifact over HTTP after a parallel byte-range attempt fails or is skipped. When the server answers the GET with a non-2xx status, the function bails immediately with the status code rather than writing an error page or empty body to the destination file. This guards the update flow from publishing corrupt artifacts (e.g. 404 HTML pages) as binaries.","triggerScenarios":"Calling download_with_progress(url, dest) where the HTTP GET returns a non-success status: 404 (artifact/tag missing), 403 (rate-limited or no access to GCS/GitHub release asset), 401, 429, 5xx server errors, or 3xx that the client does not follow to a 2xx.","commonSituations":"A release tag was renamed or deleted so the asset URL 404s; a private repo bucket returns 403 because GITHUB_TOKEN is missing/expired; GitHub API rate limiting (429) after many checks; a transient 502/503 from a CDN during a release rollout; a typo'd or outdated version URL.","solutions":["Verify the URL is correct and the release/asset exists for the requested tag (check the repo's releases or GCS bucket).","Re-run after a short delay if the status is 429 or 5xx; respect any Retry-After header.","If 403, check credentials/expiry of the token used (e.g. GITHUB_TOKEN) and repo/bucket access permissions.","Check network/proxy configuration; a captive portal or proxy can turn the GET into an error response.","As a last resort clear any cached version metadata so the updater recomputes a valid download URL."],"exampleFix":"// before: blind call with a stale URL\nlet url = format!(\"https://example.com/dist/{old_tag}/cli\");\ndownload_with_progress(&url, &dest).await?;\n\n// after: pre-check the URL / pin to a live channel metadata URL\nlet meta: ChannelMetadata = reqwest::get(&channel_url).await?.json().await?;\nlet url = meta.latest_binary_url;\ndownload_with_progress(&url, &dest).await?;","handlingStrategy":"retry","validationCode":"// Pre-check the URL resolves to a 2xx before invoking the downloader\nlet client = reqwest::Client::new();\nlet status = client.head(url).send().await?.status();\nif !status.is_success() {\n    anyhow::bail!(\"refusing to download {url}: HTTP {status}\");\n}","typeGuard":"fn is_downloadable(resp: &reqwest::Response) -> bool {\n    resp.status().is_success()\n}","tryCatchPattern":"match download_with_progress(url, &dest).await {\n    Ok(()) => println!(\"updated\"),\n    Err(e) if e.to_string().contains(\"HTTP 429\") || e.to_string().contains(\"HTTP 5\") => {\n        // exponential backoff retry\n        tokio::time::sleep(Duration::from_secs(5)).await;\n        download_with_progress(url, &dest).await?;\n    }\n    Err(e) => eprintln!(\"download failed: {e}\"), // surface status code to user\n}","preventionTips":["Resolve download URLs from live channel metadata instead of hardcoding tags.","Keep auth tokens fresh and scoped to the release bucket/repo.","Implement backoff-and-retry for 429/5xx before giving up.","Test download URLs with a HEAD request in CI pipelines before release automation runs."],"tags":["network","http","download","updater"],"backgroundTag":"http-non-success-status","analyzedSha":"bc7f02eddd3d84085849dc19ed216f11c23b0571","analyzedAt":"2026-08-31T04:59:42.031Z","schemaVersion":2},"datasetVersion":"2026-08-31T09:17:48.483Z"}