{"record":{"id":"86c093571a1ef78f","repo":"aaif-goose/goose","slug":"failed-to-download-http","errorCode":null,"errorMessage":"Failed to download: HTTP {}","messagePattern":"Failed to download: HTTP (.+?)","errorType":"http","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/goose-download-manager/src/lib.rs","lineNumber":513,"sourceCode":"\n            let status = response.status();\n            if status == reqwest::StatusCode::RANGE_NOT_SATISFIABLE {\n                if file_total > 0 && file_bytes == file_total {\n                    break;\n                }\n                *cumulative_bytes = cumulative_bytes.saturating_sub(file_bytes);\n                file_bytes = 0;\n                let _ = tokio::fs::remove_file(&partial_path).await;\n                continue;\n            }\n\n            if !status.is_success() && status != reqwest::StatusCode::PARTIAL_CONTENT {\n                let is_transient = status.is_server_error()\n                    || status == reqwest::StatusCode::REQUEST_TIMEOUT\n                    || status == reqwest::StatusCode::TOO_MANY_REQUESTS;\n\n                if !is_transient || retries >= Self::MAX_RETRIES {\n                    anyhow::bail!(\"Failed to download: HTTP {}\", status);\n                }\n                retries += 1;\n                let delay = std::cmp::min(\n                    Self::RETRY_BASE_DELAY * 2u32.saturating_pow(retries - 1),\n                    Self::RETRY_MAX_DELAY,\n                );\n                info!(model_id = %model_id, retry = retries, http_status = %status, \"Retrying download after transient HTTP error\");\n                Self::cancellable_sleep(delay, downloads, model_id).await?;\n                continue;\n            }\n\n            if file_bytes > 0 && status == reqwest::StatusCode::OK {\n                info!(model_id = %model_id, \"Server ignored Range header, restarting file from scratch\");\n                // Subtract already-counted partial bytes from cumulative\n                *cumulative_bytes = cumulative_bytes.saturating_sub(file_bytes);\n                file_bytes = 0;\n                let _ = tokio::fs::remove_file(&partial_path).await;\n            }","sourceCodeStart":495,"sourceCodeEnd":531,"githubUrl":"https://github.com/aaif-goose/goose/blob/3810898a7447ec3299be72e223d3570a7aabf0ab/crates/goose-download-manager/src/lib.rs#L495-L531","documentation":"The HTTP response status was neither a 2xx nor 206 Partial Content. Only 5xx, 408 and 429 are treated as transient and retried (up to 10 times with backoff); every other status fails immediately, as do transient statuses once retries are exhausted. The status code is embedded in the message text.","triggerScenarios":"401/403 when the repo is gated or the bearer token is missing/expired; 404 when the built URL references a nonexistent repo/file or the file was renamed; 400-class errors for malformed requests; 5xx/429 persisting past 10 retries.","commonSituations":"Downloading gated HF models without huggingface-cli login; a typo in the model spec filename; upstream repo restructure deleting the quantization file; heavy rate limiting during model-release spikes.","solutions":["Extract the status from the message: 401/403 means authenticate (log in to HuggingFace / supply the bearer token) or use a non-gated mirror","404 means verify the repo and exact filename on huggingface.co and fix the model spec","429 means back off well past the Retry-After window before restarting","5xx means the origin is unhealthy: retry later; the download resumes from the .partial file"],"exampleFix":"// before\nlet result = manager.download_model_sharded_with_bearer_token(id, files, hint, token, None).await;\n\n// after: branch on the embedded HTTP status instead of surfacing a generic error\nif let Err(e) = &result {\n    if let Some(code) = e.to_string().strip_prefix(\"Failed to download: HTTP \") {\n        match code.trim() {\n            \"401\" | \"403\" => { /* refresh HF token, then retry */ }\n            \"404\" => { /* fix repo/filename in the spec */ }\n            _ => { /* transient or unknown: schedule a later retry */ }\n        }\n    }\n}","handlingStrategy":"try-catch","validationCode":"// confirm reachability and authorization before committing to the transfer\nlet resp = client.head(url).send().await?;\nuse reqwest::StatusCode;\nmatch resp.status() {\n    s if s.is_success() => {}\n    StatusCode::UNAUTHORIZED | StatusCode::FORBIDDEN => anyhow::bail!(\"supply a valid HF token for this gated repo\"),\n    StatusCode::NOT_FOUND => anyhow::bail!(\"repo/filename wrong: verify on huggingface.co\"),\n    s => anyhow::bail!(\"preflight got HTTP {s}\"),\n}","typeGuard":null,"tryCatchPattern":"if let Some(code) = err.to_string().strip_prefix(\"Failed to download: HTTP \") {\n    match code.trim() {\n        \"401\" | \"403\" => { /* authenticate or switch to a non-gated mirror */ }\n        \"404\" => { /* fix the repo/filename in the model spec */ }\n        \"429\" => { /* back off past Retry-After, then retry */ }\n        s if s.starts_with('5') => { /* origin outage: retry later; resume is automatic */ }\n        _ => surface(err),\n    }\n}","preventionTips":["Authenticate with HuggingFace before downloading gated models so the bearer token is attached","Validate repo and exact filename against the HF API before queueing multi-GB transfers","Treat 401/403/404 as permanent (fix input) and only 5xx/408/429 as retryable"],"tags":["rust","http","download","authentication","huggingface"],"backgroundTag":null,"analyzedSha":"3810898a7447ec3299be72e223d3570a7aabf0ab","analyzedAt":"2026-08-16T10:14:26.282Z","schemaVersion":2},"datasetVersion":"2026-08-16T13:17:31.715Z"}