tonhowtf/omniget · error · anyhow::Error
Failed to fetch AES key after
Error message
Failed to fetch AES key after {} attempts What it means
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.
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.
Example fix
// before
let key = fetch_key_with_retry(client, &key_uri, referer, 3).await?;
// after
let key = fetch_key_with_retry(client, &key_uri, referer, 5)
.await
.with_context(|| format!("AES key fetch failed for {key_uri}"))?; Defensive patterns
Strategy: retry
Validate before calling
let max_retries = max_retries.max(3); // never zero attempts for encrypted streams // and pre-check reachability of the key URI with a HEAD request
Try / catch
let key = fetch_key_with_retry(client, &key_uri, referer, 5).await
.with_context(|| format!("AES key unavailable after retries: {key_uri}"))?; Prevention
- 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.
When it happens
Trigger: 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).
Common situations: Key server outage; client hard-blocked by the key CDN; extremely short-lived key URLs; too few retries against a rate-limited endpoint.
Related errors
AI-assisted analysis of tonhowtf/omniget@8600b91f42 (2026-09-12).
Data as JSON: /api/errors/c8e574cc78b49780.
Report an issue: GitHub.
Appendix: source
Thrown at src-tauri/omniget-core/src/core/hls_downloader.rs:493
if !resp.status().is_success() {
last_err = Some(anyhow::anyhow!("HTTP {} fetching AES key", resp.status()));
} else {
match resp.bytes().await {
Ok(bytes) => return Ok(bytes.to_vec()),
Err(e) => last_err = Some(anyhow::anyhow!(e)),
}
}
}
Err(e) => last_err = Some(anyhow::anyhow!(e)),
}
if attempt < max_retries - 1 {
let base = 500 * (attempt as u64 + 1);
let jitter = rand::random::<u64>() % (base / 2 + 1);
tokio::time::sleep(Duration::from_millis(base + jitter)).await;
}
}
Err(last_err.unwrap_or_else(|| {
anyhow::anyhow!("Failed to fetch AES key after {} attempts", max_retries)
}))
}
}
struct EncryptionInfo {
key_bytes: Vec<u8>,
iv: Option<[u8; 16]>,
}
/// Attach `Referer` (and a matching `Origin`) headers to a request.
/// An empty referer means "send no Referer/Origin at all" — some CDNs
/// reject requests with a wrong Referer but accept ones without any.
fn apply_referer_headers(req: reqwest::RequestBuilder, referer: &str) -> reqwest::RequestBuilder {
if referer.is_empty() {
return req;
}
let req = req.header("Referer", referer);
match url_origin(referer) {View on GitHub (pinned to 8600b91f42)