{"record":{"id":"c8e574cc78b49780","repo":"tonhowtf/omniget","slug":"failed-to-fetch-aes-key-after-attempts","errorCode":null,"errorMessage":"Failed to fetch AES key after {} attempts","messagePattern":"Failed to fetch AES key after (.+?) attempts","errorType":"exception","errorClass":"anyhow::Error","httpStatus":null,"severity":"error","filePath":"src-tauri/omniget-core/src/core/hls_downloader.rs","lineNumber":493,"sourceCode":"                    if !resp.status().is_success() {\n                        last_err = Some(anyhow::anyhow!(\"HTTP {} fetching AES key\", resp.status()));\n                    } else {\n                        match resp.bytes().await {\n                            Ok(bytes) => return Ok(bytes.to_vec()),\n                            Err(e) => last_err = Some(anyhow::anyhow!(e)),\n                        }\n                    }\n                }\n                Err(e) => last_err = Some(anyhow::anyhow!(e)),\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(Duration::from_millis(base + jitter)).await;\n            }\n        }\n        Err(last_err.unwrap_or_else(|| {\n            anyhow::anyhow!(\"Failed to fetch AES key after {} attempts\", max_retries)\n        }))\n    }\n}\n\nstruct EncryptionInfo {\n    key_bytes: Vec<u8>,\n    iv: Option<[u8; 16]>,\n}\n\n/// Attach `Referer` (and a matching `Origin`) headers to a request.\n/// An empty referer means \"send no Referer/Origin at all\" — some CDNs\n/// reject requests with a wrong Referer but accept ones without any.\nfn apply_referer_headers(req: reqwest::RequestBuilder, referer: &str) -> reqwest::RequestBuilder {\n    if referer.is_empty() {\n        return req;\n    }\n    let req = req.header(\"Referer\", referer);\n    match url_origin(referer) {","sourceCodeStart":475,"sourceCodeEnd":511,"githubUrl":"https://github.com/tonhowtf/omniget/blob/8600b91f4246848bac346874daa9e61c1fc5677a/src-tauri/omniget-core/src/core/hls_downloader.rs#L475-L511","documentation":"Terminal error of fetch_key_with_retry: all attempts to download the AES key failed and no more specific error is available, so a message naming the attempt count is returned. The download cannot proceed since segments are encrypted.","triggerScenarios":"Every retry to GET the key URI failed (transport errors or non-2xx statuses) across max_retries; effectively when last_err is None (e.g., max_retries=0).","commonSituations":"Key server outage; client hard-blocked by the key CDN; extremely short-lived key URLs; too few retries against a rate-limited endpoint.","solutions":["Log per-attempt errors to reveal the true cause (status code vs network error).","Increase max_retries/backoff, and honor Retry-After on 429.","Refresh the playlist to get a new key URI before retrying.","Verify headers/cookies required by the key server match those used for segments."],"exampleFix":"// before\nlet key = fetch_key_with_retry(client, &key_uri, referer, 3).await?;\n// after\nlet key = fetch_key_with_retry(client, &key_uri, referer, 5)\n    .await\n    .with_context(|| format!(\"AES key fetch failed for {key_uri}\"))?;","handlingStrategy":"retry","validationCode":"let max_retries = max_retries.max(3); // never zero attempts for encrypted streams\n// and pre-check reachability of the key URI with a HEAD request","typeGuard":null,"tryCatchPattern":"let key = fetch_key_with_retry(client, &key_uri, referer, 5).await\n    .with_context(|| format!(\"AES key unavailable after retries: {key_uri}\"))?;","preventionTips":["Log per-attempt causes; a bare 'after N attempts' hides the real status.","Increase retries and backoff for key CDNs that rate-limit.","Refresh the playlist for new key URIs before retrying.","Warn users early when a stream is AES-encrypted and the key server looks unreachable."],"tags":["hls","aes","retry","encryption-key"],"backgroundTag":"request-failed-after-retries","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"}