{"record":{"id":"540f74dc560ec733","repo":"cjpais/Handy","slug":"transfer-stalled-no-data-for-s","errorCode":null,"errorMessage":"transfer stalled: no data for {}s","messagePattern":"transfer stalled: no data for (.+?)s","errorType":"exception","errorClass":"anyhow::Error","httpStatus":null,"severity":"error","filePath":"src-tauri/src/managers/model/download.rs","lineNumber":327,"sourceCode":"                total: total_size,\n                percentage: if total_size > 0 {\n                    (downloaded as f64 / total_size as f64) * 100.0\n                } else {\n                    0.0\n                },\n            }));\n        };\n        emit_progress(downloaded);\n\n        // Throttle progress events to max 10/sec (100ms intervals)\n        let mut last_emit = Instant::now();\n        let throttle = Duration::from_millis(100);\n        let mut stream = response.bytes_stream();\n        loop {\n            let chunk = tokio::select! {\n                c = tokio::time::timeout(DOWNLOAD_STALL_TIMEOUT, stream.next()) => match c {\n                    // Stalled mid-body: keep the partial for resume.\n                    Err(_) => return Err(anyhow::anyhow!(\n                        \"transfer stalled: no data for {}s\",\n                        DOWNLOAD_STALL_TIMEOUT.as_secs()\n                    )),\n                    Ok(None) => break,\n                    Ok(Some(chunk)) => chunk?,\n                },\n                _ = cancel_token.cancelled() => {\n                    // Keep the partial for resume; caller handles state cleanup.\n                    return Ok(HttpDownloadOutcome::Cancelled);\n                }\n            };\n            // An untrusted server must not be able to fill the disk: cut the\n            // transfer at the first byte past the known total instead of\n            // trusting it to eventually close the stream. Everything written\n            // so far is tainted by a provably-misbehaving server — clear it.\n            if let Some(cap) = known_total {\n                if downloaded + chunk.len() as u64 > cap {\n                    drop(file);","sourceCodeStart":309,"sourceCodeEnd":345,"githubUrl":"https://github.com/cjpais/Handy/blob/c89b7bf38974ad317177389f5bfe28236f0d64e3/src-tauri/src/managers/model/download.rs#L309-L345","documentation":"Raised when the response body stream yields no chunk for DOWNLOAD_STALL_TIMEOUT (60 s) while the download is otherwise in progress. The connection is alive at the HTTP level but data stopped flowing — a mid-transfer stall. Critically, the partial file is kept on purpose, so the next download attempt resumes from where the data stopped rather than restarting a multi-GB fetch.","triggerScenarios":"stream.next() times out: server/CDN stops sending mid-body, connection half-open after a NAT/firewall drop, path MTU blackhole, or severe bandwidth starvation (not zero, but under the event loop's progress cadence — practically, nothing arrives for 60 s).","commonSituations":"Laptop sleep/resume mid-download; flaky hotel/airport Wi-Fi; mobile tether switch; CDN node failure mid-stream; power-saving NICs dropping long transfers.","solutions":["Retry the download — resume picks up from the preserved partial.","Prevent the machine from sleeping during large downloads (or use a wired connection).","If stalls repeat at the same point, capture the URL and test with curl -C - <url> to distinguish network from server issues.","Move off unreliable hotspots/VPNs for multi-GB models."],"exampleFix":null,"handlingStrategy":"retry","validationCode":null,"typeGuard":null,"tryCatchPattern":"match manager.download_model(model_id, None).await {\n    Ok(_) => Ok(()),\n    Err(e) if e.to_string().contains(\"transfer stalled\") => {\n        // partial preserved on stall: the retry resumes from where data stopped\n        manager.download_model(model_id, None).await\n    }\n    Err(e) => Err(e),\n}","preventionTips":["Disable system sleep during multi-GB downloads.","Always keep the partial on stall-timeout (as this code does) so retries are cheap.","Surface a resume affordance in the UI instead of forcing users to restart from zero.","Use stable, wired networks for first-time large model fetches."],"tags":["download","stall","timeout","network","resume"],"backgroundTag":"download-stalled","analyzedSha":"c89b7bf38974ad317177389f5bfe28236f0d64e3","analyzedAt":"2026-08-17T10:29:55.597Z","contentChangedAt":"2026-08-17T10:29:55.597Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}