{"record":{"id":"72962d76e1d4ad4e","repo":"aaif-goose/goose","slug":"download-failed-after-retries-due-to-stream-int","errorCode":null,"errorMessage":"Download failed after {} retries due to stream interruption","messagePattern":"Download failed after (.+?) retries due to stream interruption","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/goose-download-manager/src/lib.rs","lineNumber":638,"sourceCode":"                                progress.eta_seconds = eta_seconds;\n                            }\n                        }\n                    }\n                    Ok(None) => break,\n                    Err(e) => {\n                        info!(model_id = %model_id, bytes = *cumulative_bytes, error = %e, \"Download stream interrupted, will retry\");\n                        stream_error = true;\n                        break;\n                    }\n                }\n            }\n\n            file.flush().await?;\n            drop(file);\n\n            if stream_error {\n                if retries >= Self::MAX_RETRIES {\n                    anyhow::bail!(\n                        \"Download failed after {} retries due to stream interruption\",\n                        retries\n                    );\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, delay_secs = ?delay.as_secs(), \"Retrying download with resume\");\n                Self::cancellable_sleep(delay, downloads, model_id).await?;\n                continue;\n            }\n\n            break;\n        }\n\n        tokio::fs::rename(&partial_path, destination).await?;","sourceCodeStart":620,"sourceCodeEnd":656,"githubUrl":"https://github.com/aaif-goose/goose/blob/3810898a7447ec3299be72e223d3570a7aabf0ab/crates/goose-download-manager/src/lib.rs#L620-L656","documentation":"resp.chunk() failed mid-body (connection reset, premature EOF, or the 120s read timeout) and the stream-interruption retry counter reached MAX_RETRIES = 10. Each retry resumed from disk via a Range header, so bytes already written to the .partial file are preserved unless the failure path deletes them; the message reports only the exhausted retry count.","triggerScenarios":"Ten consecutive mid-body stream breaks on one file: flaky Wi-Fi/mobile links, VPN or proxy terminating long responses, NAT idle timeouts, or HF CDN connections reset repeatedly during multi-GB transfers.","commonSituations":"Laptops on unstable networks downloading sharded models; corporate proxies with aggressive response timeouts; satellite/high-latency links where 120s read stalls recur.","solutions":["Stabilize the network (wired link, different VPN endpoint) and restart the download: it resumes from the .partial file","If you control the deployment, raise the client read timeout (120s) or reduce per-file size by choosing a smaller quantization","Persist the failure and let the user retry later instead of auto-looping immediately into the same environment"],"exampleFix":null,"handlingStrategy":"retry","validationCode":null,"typeGuard":null,"tryCatchPattern":"if err.to_string().contains(\"due to stream interruption\") {\n    // ten mid-body breaks in a row: stabilize the link, then call download again -\n    // the .partial file is reused and the Range header resumes from disk\n}","preventionTips":["Prefer stable links (wired/known-good VPN) for multi-GB GGUF transfers","Restart from the same model_id so completed shards and partial bytes are reused","Do not auto-retry in a tight loop after this error; the environment, not the code, must change"],"tags":["rust","network","download","retry","streaming","resume"],"backgroundTag":null,"analyzedSha":"3810898a7447ec3299be72e223d3570a7aabf0ab","analyzedAt":"2026-08-16T10:14:26.282Z","schemaVersion":2},"datasetVersion":"2026-08-16T13:17:31.715Z"}