tonhowtf/omniget · error · anyhow::Error

connect timeout

Error message

connect timeout

What it means

download_segment sends its ranged GET (bytes=start-end) bounded by tokio::time::timeout(cfg.connect_timeout). A timeout elapsed produces this error via map_err before the reqwest error is propagated with `?`. It means the segment worker could not obtain a response within the configured connect_timeout, so the segment fails and the worker records the error and retries.

Solutions

  1. Increase cfg.connect_timeout in the fetcher configuration.
  2. Reduce the number of parallel segments/worker tasks to avoid self-inflicted saturation.
  3. Retry the download — worker_loop's retry logic may succeed on a fresh connection.
  4. Verify host reachability with `curl -m <t> -r 0-1024 <url>` to reproduce the stall independently.
Defensive patterns

Strategy: retry

Validate before calling

// pick a sane connect_timeout relative to a measured RTT
let rtt = measure_rtt(url.host_str()?)?;
let cfg = FetcherConfig { connect_timeout: Duration::from_millis((rtt * 20).max(3_000)), ..Default::default() };

Try / catch

match download(...).await {
    Err(e) if e.to_string() == "connect timeout" => {
        // worker already retries; at the top level, retry the whole download with fewer segments
    }
    other => { other?; }
}

Prevention

When it happens

Trigger: A segment's req.send() exceeds cfg.connect_timeout — slow host, packets silently dropped, or connect_timeout configured too low for the link — inside worker_loop's segment download.

Common situations: Oversubscribed parallel segment downloads saturating the local link, servers that per-connection throttle, firewalls dropping mid-download connections, or connect_timeout set to a few hundred milliseconds.

Understand the failure class

Background: Request timed out: what client-side request timeouts mean across libraries (Request timed out, TIMED_OUT, APITimeoutError) — this error's family across 39 libraries.

Related errors


AI-assisted analysis of tonhowtf/omniget@8600b91f42 (2026-09-12). Data as JSON: /api/errors/c86f5d0c975e8a55. Report an issue: GitHub.

Appendix: source

Thrown at src-tauri/omniget-core/src/core/http_fetcher.rs:964

    let end = seg.end_ceiling.load(Ordering::Relaxed);
    let already = seg.downloaded.load(Ordering::Relaxed);
    if begin + already > end {
        return Ok(());
    }
    let range_start = begin + already;

    let mut req = client.get(url);
    if let Some(h) = headers {
        req = req.headers(headers_without_range(h));
    }
    req = req.header(
        reqwest::header::RANGE,
        format!("bytes={}-{}", range_start, end),
    );

    let resp = tokio::time::timeout(cfg.connect_timeout, req.send())
        .await
        .map_err(|_| anyhow!("connect timeout"))??;

    let status = resp.status();
    if status != reqwest::StatusCode::PARTIAL_CONTENT {
        if status.is_success() {
            return Err(anyhow!("server did not honor Range (HTTP 200)"));
        }
        return Err(anyhow!("HTTP {}", status));
    }

    // A 206 alone is not proof the server gave us the slice we asked for. If it
    // answers a different offset and we write the body at ours anyway, the file
    // still ends up the right size and is silently corrupt — the one failure
    // mode a segmented download cannot detect later.
    if let Some(start) = content_range_start(resp.headers()) {
        if start != range_start {
            return Err(anyhow!(
                "server answered range at byte {} but {} was requested",
                start,

View on GitHub (pinned to 8600b91f42)