tonhowtf/omniget · error · anyhow::Error

HTTP fetching AES key

Error message

HTTP {} fetching AES key

What it means

fetch_key_with_retry downloads the AES-128 encryption key referenced by EXT-X-KEY from its URI. Non-success HTTP statuses are recorded as this error and retried with backoff; the key is required to decrypt segments, so this failure aborts decryption.

Solutions

  1. Send the same Referer/User-Agent/Cookie headers used for the playlist when fetching the key.
  2. Re-fetch the playlist (and its key URI) if the key URL is session-bound or expired.
  3. Check status: 403 -> headers/geo; 404 -> regenerate playlist URL; 429 -> back off and retry later.
  4. Fetch the key once and cache it (EXT-X-KEY usually reuses the same URI across segments) to reduce rate-limit risk.

Example fix

// before
let key = fetch_key_with_retry(client, key_url, referer, 3).await?;
// after
let key = fetch_key_with_retry(client, key_url, referer, 5)
    .await
    .with_context(|| format!("fetching AES key {key_url}"))?;
Defensive patterns

Strategy: retry

Validate before calling

// Only attempt decryption if the key looks like a 16-byte key
let key = fetch_key_with_retry(client, key_url, referer, 5).await?;
if key.len() != 16 { anyhow::bail!("key server returned {} bytes", key.len()); }

Try / catch

match fetch_key_with_retry(client, &key_uri, referer, 5).await {
    Ok(k) if k.len() == 16 => k,
    Ok(k) => bail!("AES key wrong size: {} bytes", k.len()),
    Err(e) if e.to_string().contains("HTTP 40") =>
        bail!("key denied: mirror cookies/referer from playlist request"),
    Err(e) => return Err(e.into()),
};

Prevention

When it happens

Trigger: Key server returns 403 (missing Referer/cookies), 404 (key URI expired or session-bound), 429, or 5xx while fetching the AES key URL.

Common situations: Key URIs tied to the playlist request's session/IP/cookies; expired one-time key tokens; geo-blocking of the key endpoint while segments are served openly.

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/58b587b66345cf70. Report an issue: GitHub.

Appendix: source

Thrown at src-tauri/omniget-core/src/core/hls_downloader.rs:476

            }
        }
        Ok(None)
    }

    async fn fetch_key_with_retry(
        &self,
        url: &str,
        referer: &str,
        max_retries: u32,
    ) -> anyhow::Result<Vec<u8>> {
        let mut last_err = None;
        for attempt in 0..max_retries {
            let req = apply_referer_headers(self.client.get(url), referer)
                .header("User-Agent", self.effective_user_agent());
            match req.send().await {
                Ok(resp) => {
                    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)
        }))

View on GitHub (pinned to 8600b91f42)