{"record":{"id":"d070d0dcc9d30cba","repo":"tonhowtf/omniget","slug":"timeout-downloading-segment","errorCode":null,"errorMessage":"Timeout downloading segment","messagePattern":"Timeout downloading segment","errorType":"exception","errorClass":"anyhow::Error","httpStatus":null,"severity":"error","filePath":"src-tauri/omniget-core/src/core/hls_downloader.rs","lineNumber":748,"sourceCode":"                return Err(anyhow::anyhow!(\"HTTP {} downloading segment\", code));\n            }\n\n            resp.bytes()\n                .await\n                .map(|b| b.to_vec())\n                .map_err(|e| anyhow::anyhow!(e))\n        })\n        .await;\n\n        match result {\n            Ok(Ok(data)) => return Ok(data),\n            Ok(Err(e)) => {\n                if e.to_string().contains(\"(fatal)\") {\n                    return Err(e);\n                }\n                last_err = Some(e);\n            }\n            Err(_) => last_err = Some(anyhow::anyhow!(\"Timeout downloading segment\")),\n        }\n        if attempt < max_retries - 1 {\n            let base = 500 * (attempt as u64 + 1);\n            let jitter = rand::random::<u64>() % (base / 2 + 1);\n            tokio::time::sleep(std::time::Duration::from_millis(base + jitter)).await;\n        }\n    }\n    Err(last_err.unwrap_or_else(|| {\n        anyhow::anyhow!(\"Segment download failed after {} attempts\", max_retries)\n    }))\n}\n\nconst PNG_SIGNATURE: [u8; 8] = [0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A];\nconst JPEG_SIGNATURE: [u8; 3] = [0xFF, 0xD8, 0xFF];\n\n/// Number of leading bytes to drop from a segment that arrived disguised as an\n/// image.\n///","sourceCodeStart":730,"sourceCodeEnd":766,"githubUrl":"https://github.com/tonhowtf/omniget/blob/8600b91f4246848bac346874daa9e61c1fc5677a/src-tauri/omniget-core/src/core/hls_downloader.rs#L730-L766","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"// before\nErr(_) => last_err = Some(anyhow::anyhow!(\"Timeout downloading segment\")),\n// after\nErr(_) => last_err = Some(anyhow::anyhow!(\n    \"Timeout downloading segment {} after {}ms (attempt {}/{})\",\n    seg_url, timeout_ms, attempt + 1, max_retries\n)),","handlingStrategy":"retry","validationCode":"// sanity-check the timeout budget against expected segment size and bandwidth\nlet expected_secs = (max_segment_bytes as f64 / min_bandwidth_bps as f64 * 8.0) as u64;\nif timeout.as_secs() < expected_secs {\n    log::warn!(\"segment timeout {}s likely too small; expect ~{}s\", timeout.as_secs(), expected_secs);\n}","typeGuard":null,"tryCatchPattern":"match tokio::time::timeout(timeout, download_once(url)).await {\n    Err(_) => {\n        last_err = Some(anyhow::anyhow!(\"Timeout downloading segment (attempt {}/{})\", attempt + 1, max_retries));\n        continue; // retry with backoff\n    }\n    Ok(inner) => inner?,\n}","preventionTips":["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."],"tags":["timeout","hls","network","retryable"],"backgroundTag":"request-timeout","analyzedSha":"8600b91f4246848bac346874daa9e61c1fc5677a","analyzedAt":"2026-09-12T14:29:19.317Z","contentChangedAt":"2026-09-12T14:29:19.317Z","schemaVersion":2},"datasetVersion":"2026-09-15T23:17:13.987Z"}