{"record":{"id":"5bcfa3445f75b4e9","repo":"cjpais/Handy","slug":"download-verification-failed-for-model-file-is","errorCode":null,"errorMessage":"Download verification failed for model {}: file is corrupt. Please retry.","messagePattern":"Download verification failed for model (.+?): file is corrupt\\. Please retry\\.","errorType":"exception","errorClass":"anyhow::Error","httpStatus":null,"severity":"error","filePath":"src-tauri/src/managers/model/download.rs","lineNumber":71,"sourceCode":"    /// On mismatch or read error the partial file is deleted and an error is returned,\n    /// so the next download attempt always starts from a clean state.\n    /// When `expected_sha256` is `None` (custom user models) verification is skipped.\n    fn verify_sha256(path: &Path, expected_sha256: Option<&str>, model_id: &str) -> Result<()> {\n        let Some(expected) = expected_sha256 else {\n            return Ok(());\n        };\n        match Self::compute_sha256(path) {\n            Ok(actual) if actual == expected => {\n                info!(\"SHA256 verified for model {}\", model_id);\n                Ok(())\n            }\n            Ok(actual) => {\n                warn!(\n                    \"SHA256 mismatch for model {}: expected {}, got {}\",\n                    model_id, expected, actual\n                );\n                let _ = fs::remove_file(path);\n                Err(anyhow::anyhow!(\n                    \"Download verification failed for model {}: file is corrupt. Please retry.\",\n                    model_id\n                ))\n            }\n            Err(e) => {\n                let _ = fs::remove_file(path);\n                Err(anyhow::anyhow!(\n                    \"Failed to verify download for model {}: {}. Please retry.\",\n                    model_id,\n                    e\n                ))\n            }\n        }\n    }\n\n    /// Computes the SHA256 hex digest of a file, reading in 64KB chunks to handle large models.\n    fn compute_sha256(path: &Path) -> Result<String> {\n        let mut file = File::open(path)?;","sourceCodeStart":53,"sourceCodeEnd":89,"githubUrl":"https://github.com/cjpais/Handy/blob/c89b7bf38974ad317177389f5bfe28236f0d64e3/src-tauri/src/managers/model/download.rs#L53-L89","documentation":"Raised after an HTTP model download completes but the file's SHA256 does not match the catalog's expected hash. This is the trust anchor for downloads (especially from untrusted mirrors): the bytes on disk are not the bytes the catalog signed off on. The corrupted file is deleted automatically before the error is returned, so a plain retry starts clean.","triggerScenarios":"verify_sha256 computes a hash that != expected: truncated body that still matched Content-Length (lying server), corrupted transfer via a proxy/AV scanner, a mirror serving a tampered or different build, or bit-rot on disk during hashing.","commonSituations":"Corporate proxies or antivirus re-writing downloads; Hugging Face/mirror serving a changed file while the catalog hash is stale; flaky Wi-Fi corrupting chunks that still pass size checks; disk issues on large GGUF files.","solutions":["Simply retry the download — the corrupt file is already deleted and the next attempt starts fresh.","If it fails twice at the same offset/hash, switch network (avoid intercepting proxies/VPNs) and retry.","Try the primary source instead of the mirror (or vice versa) — a consistent mismatch on one host means that host's object is wrong.","If a specific model always mismatches, the catalog hash may be outdated — report it so the hash gets regenerated.","Rule out antivirus/disk-level tampering by temporarily excluding the models directory."],"exampleFix":"// before\nmanager.download_http_resumable(id, url, &partial, size, sha, &emit).await?;\n\n// after — hash failures are self-cleaning; retry a bounded number of times\nlet mut last = None;\nfor _ in 0..3 {\n    match manager.download_http_resumable(id, url, &partial, size, sha, &emit).await {\n        Ok(out) => break,\n        Err(e) if e.to_string().contains(\"corrupt\") => { last = Some(e); continue; }\n        Err(e) => return Err(e),\n    }\n}\nif let Some(e) = last { return Err(e); }","handlingStrategy":"retry","validationCode":null,"typeGuard":null,"tryCatchPattern":"let mut attempt = 0;\nloop {\n    attempt += 1;\n    match manager.download_model(model_id, None).await {\n        Ok(_) => break,\n        Err(e) if e.to_string().contains(\"corrupt\") && attempt < 3 => {\n            warn!(\"attempt {attempt}: {e}; retrying (file was auto-deleted)\");\n            continue;\n        }\n        Err(e) => return Err(e),\n    }\n}","preventionTips":["Rely on the built-in SHA256 gate — never bypass verification for faster downloads.","Prefer the catalog's primary URL; treat mirrors as fallback only (their safety depends on this hash check).","Exclude the models directory from antivirus real-time scanning to prevent byte rewriting.","Retry with a fresh network path when two consecutive attempts mismatch."],"tags":["download","sha256","checksum","verification","model"],"backgroundTag":"checksum-verification-failed","analyzedSha":"c89b7bf38974ad317177389f5bfe28236f0d64e3","analyzedAt":"2026-08-17T10:29:55.597Z","contentChangedAt":"2026-08-17T10:29:55.597Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}