tonhowtf/omniget · error · anyhow::Error

unreachable

Error message

unreachable: {}

What it means

probe_remote first issues a HEAD request to decide whether a cheap 1-byte GET probe is needed. When the HEAD fails with a reqwest connect error (e.is_connect()), the host is definitively unreachable, so the function fails fast instead of paying a second full timeout on the GET probe. The underlying connect error is interpolated into the message.

Solutions

  1. Read the interpolated cause (DNS vs refused vs tls) to identify the exact connect failure.
  2. Verify the URL's host and port are correct and the server is listening (curl -I <url>).
  3. Check DNS resolution and firewall/proxy rules for the target host.
  4. If behind a proxy, ensure reqwest's proxy configuration matches the environment.
Defensive patterns

Strategy: retry

Validate before calling

// caller-side reachability pre-check
let host = url.host_str().ok_or("missing host")?;
let port = url.port_or_known_default().unwrap_or(80);
std::net::TcpStream::connect((host, port)).is_ok()

Try / catch

match probe(url).await {
    Err(e) if e.to_string().starts_with("unreachable:") => {
        // host down: surface a user-facing 'server unreachable' state, schedule retry with backoff
    }
    other => { other?; }
}

Prevention

When it happens

Trigger: Calling probe/probe_url for a URL whose host refuses TCP connections (port closed), fails DNS, or otherwise errors at the connect layer, and the HEAD attempt returns Ok(Err(e)) with e.is_connect() true.

Common situations: Wrong port in the URL, server down or firewall dropping traffic, DNS name no longer resolving, or testing against localhost when the service is not running.

Understand the failure class

Background: ECONNREFUSED and "connection refused" / "could not connect to server" errors: what they mean and how to fix them — this error's family across 44 libraries.

Related errors


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

Appendix: source

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

    url: &str,
    headers: Option<&reqwest::header::HeaderMap>,
    timeout: Duration,
) -> anyhow::Result<RemoteProbe> {
    let mut req = client.head(url);
    if let Some(h) = headers {
        req = req.headers(h.clone());
    }
    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)

View on GitHub (pinned to 8600b91f42)