tonhowtf/omniget · error · anyhow::Error
Timeout downloading segment
Error message
Timeout downloading segment
What it means
Raised in download_segment_with_retry when the per-attempt tokio::time::timeout around the segment request elapses before the response completes. The join/timeout wrapper returns Err(_), and the loop records this generic 'Timeout downloading segment' error and continues retrying with backoff until attempts are exhausted.
Solutions
- Increase the per-segment timeout to accommodate the largest expected segment at the target bitrate.
- Rely on the retry loop — transient stalls usually succeed on a later attempt.
- Enable HTTP range/resumable downloads for large segments instead of fetching in one request.
- Check network stability; a connection that repeatedly times out indicates a local or ISP issue.
Example fix
// before
Err(_) => last_err = Some(anyhow::anyhow!("Timeout downloading segment")),
// after
Err(_) => last_err = Some(anyhow::anyhow!(
"Timeout downloading segment {} after {}ms (attempt {}/{})",
seg_url, timeout_ms, attempt + 1, max_retries
)), Defensive patterns
Strategy: retry
Validate before calling
// sanity-check the timeout budget against expected segment size and bandwidth
let expected_secs = (max_segment_bytes as f64 / min_bandwidth_bps as f64 * 8.0) as u64;
if timeout.as_secs() < expected_secs {
log::warn!("segment timeout {}s likely too small; expect ~{}s", timeout.as_secs(), expected_secs);
} Try / catch
match tokio::time::timeout(timeout, download_once(url)).await {
Err(_) => {
last_err = Some(anyhow::anyhow!("Timeout downloading segment (attempt {}/{})", attempt + 1, max_retries));
continue; // retry with backoff
}
Ok(inner) => inner?,
} Prevention
- Size the per-segment timeout against the largest segment at the target bitrate, not an arbitrary constant.
- Prefer many small segments (shorter target duration) so a single request can't blow the budget.
- Detect stalls early with connect + read timeouts rather than one giant total timeout.
- Retest on the deployment network: mobile/VPN links often need 2-4x the desktop timeout.
When it happens
Trigger: A single segment request exceeds the configured per-segment timeout duration — slow/throttled CDN, huge segment on a poor connection, or a hung connection that never sends the body.
Common situations: Downloading over congested Wi-Fi/mobile networks; CDN throttling per-connection throughput; very long segments (e.g., 10s+ 1080p chunks) exceeding a tight timeout; server accepting the connection but stalling the body.
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.
- Timeouts: ETIMEDOUT, deadlines, and hung requests — what actually expires when a request times out.
Related errors
- HTTP downloading segment
- HTTP fetching playlist
- Failed to download attachment
- Download timeout — no data received for 30 seconds
- HTTP (fatal) downloading segment
AI-assisted analysis of tonhowtf/omniget@8600b91f42 (2026-09-12).
Data as JSON: /api/errors/d070d0dcc9d30cba.
Report an issue: GitHub.
Appendix: source
Thrown at src-tauri/omniget-core/src/core/hls_downloader.rs:748
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")),
}
if attempt < max_retries - 1 {
let base = 500 * (attempt as u64 + 1);
let jitter = rand::random::<u64>() % (base / 2 + 1);
tokio::time::sleep(std::time::Duration::from_millis(base + jitter)).await;
}
}
Err(last_err.unwrap_or_else(|| {
anyhow::anyhow!("Segment download failed after {} attempts", max_retries)
}))
}
const PNG_SIGNATURE: [u8; 8] = [0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A];
const JPEG_SIGNATURE: [u8; 3] = [0xFF, 0xD8, 0xFF];
/// Number of leading bytes to drop from a segment that arrived disguised as an
/// image.
///View on GitHub (pinned to 8600b91f42)