cjpais/Handy · error · anyhow::Error

no response within {}s from {}

Error message

no response within {}s from {}

What it means

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!.

Source

Thrown at src-tauri/src/managers/model/download.rs:228

        if resume_from > 0 {
            info!(
                "Resuming download of {} from byte {}",
                model_id, resume_from
            );
        } else {
            info!("Starting fresh download of {} from {}", model_id, url);
        }

        let client = reqwest::Client::builder()
            .connect_timeout(HTTP_CONNECT_TIMEOUT)
            .build()?;
        let mut request = client.get(url);
        if resume_from > 0 {
            request = request.header("Range", format!("bytes={}-", resume_from));
        }
        let response = tokio::select! {
            r = tokio::time::timeout(DOWNLOAD_STALL_TIMEOUT, request.send()) => r
                .map_err(|_| anyhow::anyhow!(
                    "no response within {}s from {}",
                    DOWNLOAD_STALL_TIMEOUT.as_secs(), url
                ))??,
            _ = cancel_token.cancelled() => return Ok(HttpDownloadOutcome::Cancelled),
        };

        // 416 to our Range request means its start is at or past the object's
        // end. With a catalog size in hand that can only mean the server's
        // object is *smaller* than expected (a full-size partial never issues
        // a request — handled above), and with no hash there is no trusted
        // signal to bless the partial: both restart clean. Only a hash can
        // genuinely finish a partial here. Without a Range in flight a 416 is
        // just a broken server, which the generic status check below rejects.
        if resume_from > 0 && response.status() == reqwest::StatusCode::RANGE_NOT_SATISFIABLE {
            if expected_size.is_some() || expected_sha256.is_none() {
                let _ = fs::remove_file(partial_path);
                return Err(anyhow::anyhow!(
                    "server object ends before the expected size (HTTP 416)"

View on GitHub (pinned to 98a4d80cce)

Solutions

  1. Retry — the download is resumable and often succeeds on a healthier route
  2. Verify reachability from the same machine: curl -I --max-time 70 <url>
  3. Switch the model source (e.g. to the official HuggingFace repo) if a mirror consistently stalls
  4. If you control a legitimately slow host, raise DOWNLOAD_STALL_TIMEOUT in download.rs
Defensive patterns

Strategy: retry

Validate before calling

// Cheap preflight before committing to a long download
async fn url_responsive(url: &str) -> bool {
    reqwest::Client::builder()
        .connect_timeout(Duration::from_secs(15))
        .build()
        .unwrap()
        .head(url)
        .send()
        .await
        .map(|r| r.status().is_success() || r.status().as_u16() == 206)
        .unwrap_or(false)
}

Try / catch

let mut backoff = Duration::from_secs(2);
for _ in 0..3 {
    match downloader.download_http_resumable(...).await {
        Ok(outcome) => break Ok(outcome),
        Err(e) if e.to_string().starts_with("no response within") => {
            tokio::time::sleep(backoff).await;      // exponential backoff
            backoff *= 2;
            continue;                               // resume keeps progress
        }
        Err(e) => break Err(e),
    }
}

Prevention

When it happens

Trigger: 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.

Common situations: Corporate proxies that hold requests; overloaded mirror hosts; mobile/tethered networks with long round trips; firewalls silently dropping packets.

Related errors


AI-assisted analysis of cjpais/Handy@98a4d80cce (2026-08-16). Data as JSON: /api/errors/1afb3731c900b2ff. Report an issue: GitHub.