tonhowtf/omniget · error · anyhow::Error
HTTP (fatal) downloading segment
Error message
HTTP {} (fatal) downloading segment What it means
Raised in download_segment_with_retry when the segment HTTP response status is a 4xx client error other than 429 (rate limit) and 408 (request timeout). These are treated as fatal: retrying the same URL will not help, so the error propagates immediately without further retries. The status code is embedded in the message for diagnosis.
Solutions
- Re-fetch the media playlist and use fresh segment URLs (especially for live streams where segments are pruned).
- For 401/403, refresh the auth token or re-sign the URL before downloading.
- Check whether the playlist is behind geo/IP restrictions or requires cookies/headers you are not sending.
- If segments vanish quickly on live streams, reduce the download lag behind the live edge.
Example fix
// before
if (400..500).contains(&code) && code != 429 && code != 408 {
return Err(anyhow::anyhow!("HTTP {} (fatal) downloading segment", code));
}
// after
if (400..500).contains(&code) && code != 429 && code != 408 {
if code == 403 || code == 401 {
// refresh signed URL / token once, then retry instead of failing outright
let fresh = refresh_segment_url(&seg_url).await?;
return download_once(&fresh).await;
}
return Err(anyhow::anyhow!("HTTP {} (fatal) downloading segment {}", code, seg_url));
} Defensive patterns
Strategy: validation
Validate before calling
// after resp, before treating as fatal
let code = resp.status().as_u16();
if (400..500).contains(&code) && code != 429 && code != 408 {
if code == 401 || code == 403 {
// refresh auth token / signed URL once before giving up
refresh_credentials().await?;
} else if code == 404 {
// re-fetch playlist: live segments may have moved to a new URL
refresh_playlist().await?;
}
} Try / catch
match result {
Err(e) if e.to_string().contains("(fatal)") => {
let code: u16 = /* parse from message */ 0;
if code == 403 || code == 401 {
// refresh auth and retry the segment once
} else {
return Err(e); // unrecoverable client error
}
}
other => other?,
} Prevention
- Re-fetch the media playlist periodically on live streams so segment URLs stay fresh.
- Refresh signed-URL tokens/auth cookies before they expire during long downloads.
- Send required headers (Referer, Origin, User-Agent, cookies) that the CDN enforces.
- Log the segment URL and status code together so 403/404 causes are immediately diagnosable.
When it happens
Trigger: Segment GET returns 404 (segment pruned from a live playlist), 403 (expired signed URL/token), or 401 (missing/invalid auth) — any 4xx except 429/408.
Common situations: Live stream VOD edge where old segments expire while downloading; signed CDN URLs (e.g., AWS CloudFront, Akamai) whose query-token expired mid-download; geo-blocked or auth-required manifests; HLS playlist referencing segments removed by the server.
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/0f6fa361e7520167.
Report an issue: GitHub.
Appendix: source
Thrown at src-tauri/omniget-core/src/core/hls_downloader.rs:728
cancel: &CancellationToken,
) -> anyhow::Result<Vec<u8>> {
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);View on GitHub (pinned to 8600b91f42)