{"record":{"id":"1afb3731c900b2ff","repo":"cjpais/Handy","slug":"no-response-within-s-from","errorCode":null,"errorMessage":"no response within {}s from {}","messagePattern":"no response within (.+?)s from (.+?)","errorType":"exception","errorClass":"anyhow::Error","httpStatus":null,"severity":"error","filePath":"src-tauri/src/managers/model/download.rs","lineNumber":228,"sourceCode":"        if resume_from > 0 {\n            info!(\n                \"Resuming download of {} from byte {}\",\n                model_id, resume_from\n            );\n        } else {\n            info!(\"Starting fresh download of {} from {}\", model_id, url);\n        }\n\n        let client = reqwest::Client::builder()\n            .connect_timeout(HTTP_CONNECT_TIMEOUT)\n            .build()?;\n        let mut request = client.get(url);\n        if resume_from > 0 {\n            request = request.header(\"Range\", format!(\"bytes={}-\", resume_from));\n        }\n        let response = tokio::select! {\n            r = tokio::time::timeout(DOWNLOAD_STALL_TIMEOUT, request.send()) => r\n                .map_err(|_| anyhow::anyhow!(\n                    \"no response within {}s from {}\",\n                    DOWNLOAD_STALL_TIMEOUT.as_secs(), url\n                ))??,\n            _ = cancel_token.cancelled() => return Ok(HttpDownloadOutcome::Cancelled),\n        };\n\n        // 416 to our Range request means its start is at or past the object's\n        // end. With a catalog size in hand that can only mean the server's\n        // object is *smaller* than expected (a full-size partial never issues\n        // a request — handled above), and with no hash there is no trusted\n        // signal to bless the partial: both restart clean. Only a hash can\n        // genuinely finish a partial here. Without a Range in flight a 416 is\n        // just a broken server, which the generic status check below rejects.\n        if resume_from > 0 && response.status() == reqwest::StatusCode::RANGE_NOT_SATISFIABLE {\n            if expected_size.is_some() || expected_sha256.is_none() {\n                let _ = fs::remove_file(partial_path);\n                return Err(anyhow::anyhow!(\n                    \"server object ends before the expected size (HTTP 416)\"","sourceCodeStart":210,"sourceCodeEnd":246,"githubUrl":"https://github.com/cjpais/Handy/blob/98a4d80cce8ad41efec2a419b59d9e81229a35d7/src-tauri/src/managers/model/download.rs#L210-L246","documentation":"The initial GET (with an optional Range header for resume) did not produce a response within DOWNLOAD_STALL_TIMEOUT — 60 seconds, defined at download.rs:26 (a separate 15s HTTP_CONNECT_TIMEOUT governs connection setup). The downloader refuses to wait indefinitely for headers; the cancel token is honored concurrently via tokio::select!.","triggerScenarios":"Server or CDN slow or hung before sending response headers; network black-holed after connect (VPN drop, captive portal); DNS+TLS+headers exceeding 60s on very slow links; a dead mirror that accepts connections then stalls.","commonSituations":"Corporate proxies that hold requests; overloaded mirror hosts; mobile/tethered networks with long round trips; firewalls silently dropping packets.","solutions":["Retry — the download is resumable and often succeeds on a healthier route","Verify reachability from the same machine: curl -I --max-time 70 <url>","Switch the model source (e.g. to the official HuggingFace repo) if a mirror consistently stalls","If you control a legitimately slow host, raise DOWNLOAD_STALL_TIMEOUT in download.rs"],"exampleFix":null,"handlingStrategy":"retry","validationCode":"// Cheap preflight before committing to a long download\nasync fn url_responsive(url: &str) -> bool {\n    reqwest::Client::builder()\n        .connect_timeout(Duration::from_secs(15))\n        .build()\n        .unwrap()\n        .head(url)\n        .send()\n        .await\n        .map(|r| r.status().is_success() || r.status().as_u16() == 206)\n        .unwrap_or(false)\n}","typeGuard":null,"tryCatchPattern":"let mut backoff = Duration::from_secs(2);\nfor _ in 0..3 {\n    match downloader.download_http_resumable(...).await {\n        Ok(outcome) => break Ok(outcome),\n        Err(e) if e.to_string().starts_with(\"no response within\") => {\n            tokio::time::sleep(backoff).await;      // exponential backoff\n            backoff *= 2;\n            continue;                               // resume keeps progress\n        }\n        Err(e) => break Err(e),\n    }\n}","preventionTips":["Mirror the 60s stall budget in any preflight health check before starting big downloads","Prefer resumable endpoints so timeouts never restart from byte zero","For slow private hosts, size DOWNLOAD_STALL_TIMEOUT to the worst realistic header latency"],"tags":["http","timeout","network","download"],"backgroundTag":"request-timeout","analyzedSha":"98a4d80cce8ad41efec2a419b59d9e81229a35d7","analyzedAt":"2026-08-16T20:58:09.966Z","schemaVersion":2},"datasetVersion":"2026-08-16T23:17:17.608Z"}