tonhowtf/omniget · error
Size mismatch: expected {} bytes, got {}
Error message
Size mismatch: expected {} bytes, got {} What it means
download_attempt verifies the fully downloaded .part file against the Content-Length the server advertised during the probe. If the byte count on disk differs from the expected content length (and expected > 0), the partial file is deleted and this error is returned. It guards against truncated or corrupted transfers (e.g. connection dropped mid-body without a TCP error).
Solutions
- Re-run the download — the retry wrapper deletes the .part file, so a fresh attempt re-probes Content-Length and starts clean.
- Check free disk space on the output volume; a full disk can produce short writes.
- Test the URL with curl and compare Content-Length vs downloaded size to confirm the server itself is misbehaving.
- If resuming, delete the stale .part file manually (output + '.part') so the next attempt starts from byte 0 instead of a corrupt offset.
- If a specific server chronically sends wrong Content-Length, consider disabling length verification or switching mirrors.
Example fix
// before
let part = PathBuf::from(format!("{}.part", out.display()));
if part.exists() { /* keep it and hope resume works */ }
let file = downloader.download(&url, &out).await?;
// after
let part = PathBuf::from(format!("{}.part", out.display()));
if part.exists() {
let meta = std::fs::metadata(&part)?;
if meta.len() == 0 || meta.modified().ok().map_or(true, |m| m.elapsed().map_or(true, |e| e.as_secs() > 3600)) {
let _ = std::fs::remove_file(&part); // stale/corrupt resume data -> avoid size mismatch
}
}
let file = downloader.download(&url, &out).await?; Defensive patterns
Strategy: validation
Validate before calling
// Rust: compare the advertised Content-Length to what a quick range probe returns
async fn length_agrees(client: &reqwest::Client, url: &str) -> Option<bool> {
let r = client.head(url).send().await.ok()?;
let len: u64 = r.headers().get("content-length")?.to_str().ok()?.parse().ok()?;
Some(len > 0)
} // mismatch on resume: delete stale '<output>.part' before calling download Try / catch
match downloader.download(&url, &out).await {
Err(e) if e.to_string().starts_with("Size mismatch:") => {
let _ = std::fs::remove_file(out.with_extension("part"));
downloader.download(&url, &out).await // clean restart
}
other => other,
} Prevention
- Delete stale .part files whose age or source URL doesn't match the current download
- Ensure adequate free disk space before large downloads
- Don't reuse the same output path for different URLs
- If a server chronically lies about Content-Length, verify it once with curl and avoid that mirror
When it happens
Trigger: The server's probe response advertised a content_length > 0, but the bytes written to part_path after the stream completed (or broke) equal a different number — truncated download, server sending fewer bytes than advertised, or a resume that started from the wrong offset.
Common situations: Connection dropped mid-transfer and the stream ended 'cleanly' without an error; a CDN/edge node serving a truncated body; resuming a .part file that was written by a different/older request whose range the server silently ignored; disk full causing short writes in unusual setups.
Understand the failure class
Background: Checksum mismatch errors: "checksum verification failed", "digest mismatch", "expected vs actual checksum" — what they mean and how to fix them — this error's family across 41 libraries.
Related errors
- download de {} falhou: HTTP {}
- nao foi possivel buscar {}: {}
- Server returned HTML instead of media — the link may have ex
- Range not satisfiable, restarting
- HTTP {} downloading {}
AI-assisted analysis of tonhowtf/omniget@8600b91f42 (2026-09-12).
Data as JSON: /api/errors/ccb9ce60ccf66f02.
Report an issue: GitHub.
Appendix: source
Thrown at src-tauri/omniget-core/src/core/direct_downloader.rs:314
};
download_single_stream(
client,
url,
&part_path,
existing,
probe.content_length,
progress_tx,
headers,
cancel,
)
.await?;
}
if let Some(expected) = probe.content_length {
let actual = std::fs::metadata(&part_path)?.len();
if expected > 0 && actual != expected {
let _ = std::fs::remove_file(&part_path);
return Err(anyhow!(
"Size mismatch: expected {} bytes, got {}",
expected,
actual
));
}
}
if let Err(e) = reject_html_masquerading_as_media(&part_path) {
let _ = std::fs::remove_file(&part_path);
return Err(e);
}
std::fs::rename(&part_path, output)?;
let _ = progress_tx.send(ProgressUpdate::percent(100.0)).await;
let size = std::fs::metadata(output)?.len();
Ok(size)
}View on GitHub (pinned to 8600b91f42)