cjpais/Handy · error · anyhow::Error

Download verification failed for model {}: file is corrupt.

Error message

Download verification failed for model {}: file is corrupt. Please retry.

What it means

After an HTTP download completes, the file's SHA256 is computed in 64KB chunks and compared against the catalog's pinned hash. A mismatch means the bytes on disk are not the published model — a corrupt transfer, a tampered or misbehaving mirror, or a catalog hash that no longer matches a re-published upstream file. The file is deleted and the caller is told to retry.

Source

Thrown at src-tauri/src/managers/model/download.rs:71

    /// On mismatch or read error the partial file is deleted and an error is returned,
    /// so the next download attempt always starts from a clean state.
    /// When `expected_sha256` is `None` (custom user models) verification is skipped.
    fn verify_sha256(path: &Path, expected_sha256: Option<&str>, model_id: &str) -> Result<()> {
        let Some(expected) = expected_sha256 else {
            return Ok(());
        };
        match Self::compute_sha256(path) {
            Ok(actual) if actual == expected => {
                info!("SHA256 verified for model {}", model_id);
                Ok(())
            }
            Ok(actual) => {
                warn!(
                    "SHA256 mismatch for model {}: expected {}, got {}",
                    model_id, expected, actual
                );
                let _ = fs::remove_file(path);
                Err(anyhow::anyhow!(
                    "Download verification failed for model {}: file is corrupt. Please retry.",
                    model_id
                ))
            }
            Err(e) => {
                let _ = fs::remove_file(path);
                Err(anyhow::anyhow!(
                    "Failed to verify download for model {}: {}. Please retry.",
                    model_id,
                    e
                ))
            }
        }
    }

    /// Computes the SHA256 hex digest of a file, reading in 64KB chunks to handle large models.
    fn compute_sha256(path: &Path) -> Result<String> {
        let mut file = File::open(path)?;

View on GitHub (pinned to 98a4d80cce)

Solutions

  1. Retry download_model — a fresh transfer often resolves transient corruption
  2. If it persists, verify the catalog's sha256 against the publisher (e.g. the HF repo's LFS sha256) and update the catalog
  3. Bypass the misbehaving mirror and use the official HuggingFace source
  4. Check disk health and free space; disable interfering proxies

Example fix

// before
let outcome = downloader.download_http_resumable(...).await?;

// after — corrupt file is deleted by the verifier; a fresh attempt is safe
let mut attempt = 0;
let outcome = loop {
    attempt += 1;
    match downloader.download_http_resumable(...).await {
        Ok(o) => break o,
        Err(e) if e.to_string().contains("file is corrupt") && attempt < 3 => continue,
        Err(e) => return Err(e),
    }
};
Defensive patterns

Strategy: retry

Validate before calling

// Before reusing an already-downloaded file, verify it against the pinned hash
fn model_file_intact(path: &Path, expected_sha256: Option<&str>) -> bool {
    match expected_sha256 {
        Some(expected) => ModelDownload::compute_sha256(path)
            .map(|actual| actual == expected)
            .unwrap_or(false),
        None => true, // no pinned hash — nothing to validate against
    }
}

Try / catch

let mut attempts = 0;
loop {
    attempts += 1;
    match downloader.download_http_resumable(...).await {
        Ok(outcome) => break Ok(outcome),
        Err(e) if e.to_string().contains("file is corrupt") && attempts < 3 => {
            // verifier already deleted the corrupt file; retry clean
            continue;
        }
        Err(e) => break Err(e),
    }
}

Prevention

When it happens

Trigger: A mirror host serving wrong or padded content that still matches the expected size; a TLS-terminating proxy corrupting bytes; upstream re-published the file while the catalog still pins the old sha256; disk corruption during the write.

Common situations: Custom mirror URLs that go stale when a model is updated upstream; flaky transfers where size checks pass but content differs; catalog/app version skew after a model re-release.

Related errors


AI-assisted analysis of cjpais/Handy@98a4d80cce (2026-08-16). Data as JSON: /api/errors/5bcfa3445f75b4e9. Report an issue: GitHub.