tonhowtf/omniget · error · anyhow::Error

unreachable: connect timed out after

Error message

unreachable: connect timed out after {:?}

What it means

The HEAD request in probe_remote is wrapped in tokio::time::timeout. When the future does not complete within the configured timeout duration, the match falls into the Err(_) arm and the library returns this error, naming the elapsed duration. It indicates the host did not even establish/answer within the connect window.

Solutions

  1. Increase the timeout passed to probe_remote if the host is known to be slow.
  2. Confirm basic reachability with `curl -m <timeout> -I <url>` to reproduce outside the library.
  3. Check for firewall rules silently dropping packets (drop vs reject produces timeouts instead of refusals).
  4. Retry later — the target host may be temporarily overloaded or down.
Defensive patterns

Strategy: retry

Try / catch

match probe_remote(client, url, headers, timeout).await {
    Err(e) if e.to_string().contains("connect timed out") => {
        // retry once with a larger timeout before giving up
    }
    other => { other?; }
}

Prevention

When it happens

Trigger: Calling probe/probe_url against a host that accepts no response within `timeout` — e.g. a firewalled host that silently drops SYN packets, or an overloaded server — so tokio::time::timeout fires on req.send().

Common situations: proof.ovh.net-style hosts that hang instead of refusing (the code comment cites a 495 s hang), over-aggressive timeout values on slow links, or packets dropped by middleboxes causing black-hole connects.

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/ed419f624c27e3f6. Report an issue: GitHub.

Appendix: source

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

    }
    let head = match tokio::time::timeout(timeout, req.send()).await {
        Ok(Ok(r)) if r.status().is_success() => Some(r),
        Ok(Ok(r)) => {
            tracing::debug!("[http_fetcher] HEAD returned HTTP {}", r.status());
            None
        }
        // Sem conexão não adianta tentar o GET de 1 byte: é o mesmo host.
        // Falhar aqui poupa um segundo timeout inteiro por tentativa quando o
        // servidor está fora (proof.ovh.net levou 495 s para desistir).
        Ok(Err(e)) if e.is_connect() => {
            return Err(anyhow!("unreachable: {}", e));
        }
        Ok(Err(e)) => {
            tracing::debug!("[http_fetcher] HEAD failed: {}", e);
            None
        }
        Err(_) => {
            return Err(anyhow!(
                "unreachable: connect timed out after {:?}",
                timeout
            ));
        }
    };

    if let Some(resp) = head {
        let h = resp.headers();
        let content_length = header_content_length(h);
        let accept_ranges = h
            .get(reqwest::header::ACCEPT_RANGES)
            .and_then(|v| v.to_str().ok())
            .map(|v| v.contains("bytes"))
            .unwrap_or(false);
        if content_length.is_some() {
            return Ok(RemoteProbe {
                content_length,
                accept_ranges,

View on GitHub (pinned to 8600b91f42)