tonhowtf/omniget · error · anyhow::Error
server returned HTML instead of media
Error message
server returned HTML instead of media
What it means
download_segment checks the Content-Type of the 206 response; if it is text/html the server is serving an error/interstitial page (login, block page, 404 HTML) instead of the file bytes, which must not be written into the target segment.
Solutions
- Check network connectivity / captive-portal status and re-authenticate
- Verify the URL is correct and still valid (HTML pages often mask 403/404)
- Bypass or reconfigure intercepting proxies/SSL-inspection appliances
- Retry the segment; if HTML persists, fail the download with a clear network-error message
Example fix
// response: Content-Type: text/html -> block/login page
// fix: detect portal and retry after re-auth
if body.starts_with(b"<") { reconnect_to_portal(); retry_segment(); } Defensive patterns
Strategy: type-guard
Validate before calling
let ct = resp.headers().get(reqwest::header::CONTENT_TYPE)
.and_then(|v| v.to_str().ok()).unwrap_or("");
if ct.contains("text/html") {
eprintln!("captive portal or error page; abort download");
return;
} Type guard
fn is_media_response(resp: &reqwest::Response) -> bool {
resp.headers().get(reqwest::header::CONTENT_TYPE)
.and_then(|v| v.to_str().ok())
.map(|s| !s.contains("text/html"))
.unwrap_or(true)
} Try / catch
match download(url).await {
Err(e) if e.to_string().contains("HTML instead of media") => {
show_captive_portal_prompt(); // ask user to log in / fix network
}
Err(e) => return Err(e),
Ok(f) => f,
} Prevention
- Detect captive portals before starting downloads (probe a known endpoint)
- Validate Content-Type on HEAD before segmenting
- Surface a user-friendly 'network blocked' message instead of raw error
- Whitelist/bypass SSL-inspection proxies for the download host
When it happens
Trigger: The ranged GET returns 206/200 with Content-Type: text/html — typically a captive portal, antivirus/proxy block page, SSO login redirect, or a soft-404 HTML page.
Common situations: Hotel/office captive portals intercepting HTTPS-failed requests; expired signed URLs returning an HTML error page; corporate proxies injecting HTML; hotspot DNS hijacking.
Related errors
- Server returned HTML instead of media — URL may have expired
- Access denied (403). The video may be private or…
- acesso negado (HTTP ). Capture os cookies da sua conta no…
- criar perfil
- describe
AI-assisted analysis of tonhowtf/omniget@8600b91f42 (2026-09-12).
Data as JSON: /api/errors/2ef46f85fa5fb36d.
Report an issue: GitHub.
Appendix: source
Thrown at src-tauri/omniget-core/src/core/http_fetcher.rs:991
// A 206 alone is not proof the server gave us the slice we asked for. If it
// answers a different offset and we write the body at ours anyway, the file
// still ends up the right size and is silently corrupt — the one failure
// mode a segmented download cannot detect later.
if let Some(start) = content_range_start(resp.headers()) {
if start != range_start {
return Err(anyhow!(
"server answered range at byte {} but {} was requested",
start,
range_start
));
}
}
if let Some(ct) = resp.headers().get(reqwest::header::CONTENT_TYPE) {
if let Ok(s) = ct.to_str() {
if s.contains("text/html") {
return Err(anyhow!("server returned HTML instead of media"));
}
}
}
let mut file = tokio::fs::OpenOptions::new()
.write(true)
.open(part_path)
.await?;
file.seek(std::io::SeekFrom::Start(range_start)).await?;
let mut stream = resp.bytes_stream();
let mut written: u64 = 0;
let started = std::time::Instant::now();
tracing::debug!(
"[http_fetcher] segment {}: GET bytes={}-{} ({} bytes)",
seg.id,
range_start,
end,View on GitHub (pinned to 8600b91f42)