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

  1. Re-run the download — the retry wrapper deletes the .part file, so a fresh attempt re-probes Content-Length and starts clean.
  2. Check free disk space on the output volume; a full disk can produce short writes.
  3. Test the URL with curl and compare Content-Length vs downloaded size to confirm the server itself is misbehaving.
  4. If resuming, delete the stale .part file manually (output + '.part') so the next attempt starts from byte 0 instead of a corrupt offset.
  5. 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

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


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)