tonhowtf/omniget · error · anyhow::Error

HTTP downloading segment

Error message

HTTP {} downloading segment

What it means

Raised in download_segment_with_retry when the segment response status is a non-success that is NOT treated as fatal: any 5xx server error, 429 rate limit, or 408 request timeout. Unlike the fatal branch, the caller's retry loop keeps retrying this segment with exponential backoff. The status code is included in the message.

Solutions

  1. Let the built-in retry with jittered backoff handle it; if it still fails, lower concurrent segment downloads to reduce 429s.
  2. Respect Retry-After headers on 429/503 responses when backing off.
  3. Retry the whole download later — 5xx are typically transient server-side issues.
  4. Use a different CDN mirror/edge or fallback rendition URL if the provider offers one.

Example fix

// before
return Err(anyhow::anyhow!("HTTP {} downloading segment", code));
// after
if code == 429 {
    if let Some(ra) = resp.headers().get(reqwest::header::RETRY_AFTER).and_then(|v| v.to_str().ok()) {
        let secs: u64 = ra.parse().unwrap_or(2);
        tokio::time::sleep(std::time::Duration::from_secs(secs)).await;
    }
}
return Err(anyhow::anyhow!("HTTP {} downloading segment {} (retryable)", code, seg_url));
Defensive patterns

Strategy: retry

Validate before calling

// classify status before deciding fate
let code = resp.status().as_u16();
let retryable = code >= 500 || code == 429 || code == 408;
if retryable {
    if let Some(retry_after) = resp.headers().get("Retry-After").and_then(|v| v.to_str().ok()) {
        let secs: u64 = retry_after.parse().unwrap_or(1);
        tokio::time::sleep(std::time::Duration::from_secs(secs)).await;
    }
}

Try / catch

match result {
    Err(e) if !e.to_string().contains("(fatal)") => {
        // retryable: keep last_err and back off with jitter
        last_err = Some(e);
        tokio::time::sleep(jittered_backoff(attempt)).await;
    }
    Err(e) => return Err(e),
    Ok(v) => return Ok(v),
}

Prevention

When it happens

Trigger: Segment GET returns 500/502/503/504 from the CDN or origin, or 429 Too Many Requests / 408 Request Timeout. After exhausting retries the loop surfaces the last such error.

Common situations: CDN under load (502/503 from CloudFront/Akamai edge), server-side rate limiting (429) when downloading many parallel segments, origin timeouts (408/504) on slow storage backends, transient upstream failures during live events.

Understand the failure class

Background: "API error: {status}" and "HTTP 401/403/404/429/5xx" errors: non-2xx HTTP responses explained — this error's family across 27 libraries.

Related errors


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

Appendix: source

Thrown at src-tauri/omniget-core/src/core/hls_downloader.rs:730

    let mut last_err = None;
    for attempt in 0..max_retries {
        if cancel.is_cancelled() {
            anyhow::bail!("Download cancelled");
        }

        let result = tokio::time::timeout(SEGMENT_TIMEOUT, async {
            let resp = apply_referer_headers(client.get(url), referer)
                .header("User-Agent", user_agent)
                .send()
                .await?;

            let status = resp.status();
            if !status.is_success() {
                let code = status.as_u16();
                if (400..500).contains(&code) && code != 429 && code != 408 {
                    return Err(anyhow::anyhow!("HTTP {} (fatal) downloading segment", code));
                }
                return Err(anyhow::anyhow!("HTTP {} downloading segment", code));
            }

            resp.bytes()
                .await
                .map(|b| b.to_vec())
                .map_err(|e| anyhow::anyhow!(e))
        })
        .await;

        match result {
            Ok(Ok(data)) => return Ok(data),
            Ok(Err(e)) => {
                if e.to_string().contains("(fatal)") {
                    return Err(e);
                }
                last_err = Some(e);
            }
            Err(_) => last_err = Some(anyhow::anyhow!("Timeout downloading segment")),

View on GitHub (pinned to 8600b91f42)