{"record":{"id":"d0313d50695c48f8","repo":"herdrdev/herdr","slug":"sha256-mismatch-expected-expected-got-actual","errorCode":null,"errorMessage":"sha256 mismatch: expected {expected}, got {actual}","messagePattern":"sha256 mismatch: expected (.+?), got (.+?)","errorType":"validation","errorClass":"io::Error","httpStatus":null,"severity":"error","filePath":"src/checksum.rs","lineNumber":20,"sourceCode":"    fs::File,\n    io::{self, Read},\n    path::Path,\n};\n\nuse sha2::{Digest, Sha256};\n\npub(crate) fn verify_sha256(path: &Path, expected: &str) -> io::Result<()> {\n    let expected = expected.trim().to_ascii_lowercase();\n    if expected.len() != 64 || !expected.chars().all(|ch| ch.is_ascii_hexdigit()) {\n        return Err(io::Error::new(\n            io::ErrorKind::InvalidData,\n            \"expected sha256 must be 64 hexadecimal characters\",\n        ));\n    }\n\n    let actual = file_sha256(path)?;\n    if actual != expected {\n        return Err(io::Error::new(\n            io::ErrorKind::InvalidData,\n            format!(\"sha256 mismatch: expected {expected}, got {actual}\"),\n        ));\n    }\n    Ok(())\n}\n\nfn file_sha256(path: &Path) -> io::Result<String> {\n    let mut file = File::open(path)?;\n    let mut hasher = Sha256::new();\n    let mut buffer = [0u8; 64 * 1024];\n    loop {\n        let read = file.read(&mut buffer)?;\n        if read == 0 {\n            break;\n        }\n        hasher.update(&buffer[..read]);\n    }","sourceCodeStart":2,"sourceCodeEnd":38,"githubUrl":"https://github.com/herdrdev/herdr/blob/f457cff4f2648eee85d176f8a41861241d4e8428/src/checksum.rs#L2-L38","documentation":"verify_sha256 compares a file's computed SHA-256 digest against an expected hex string and throws this io::Error(InvalidData) when they differ. The library uses it to validate downloaded or vendored assets before use. A mismatch means the file on disk is corrupted, truncated, from a different version, or the expected checksum constant is wrong.","triggerScenarios":"Calling verify_sha256(path, expected) where file_sha256(path) succeeds but returns a digest different from the 64-hex-character expected value (src/checksum.rs:20). Typical after a partial download, a re-uploaded artifact, or editing a vendored file without updating the pinned checksum.","commonSituations":"Interrupted update downloads, CDN/proxy corruption, stale checksum constants after bumping an asset version, or manually patched vendor files.","solutions":["Re-download or restore the file (e.g. herdr update) so it matches the pinned checksum","Recompute the real digest with sha256sum and compare against the expected constant to identify which side is wrong","If the file was intentionally changed, update the expected checksum constant to the new 64-hex digest","Check for proxy/antivirus tampering or disk corruption if digests differ on every download"],"exampleFix":"// before\nverify_sha256(&bundle_path, PINNED_SHA256)?;\n\n// after\n// regenerate the pin after intentionally updating the artifact\nconst PINNED_SHA256: &str = \"<new 64-hex digest>\";\nverify_sha256(&bundle_path, PINNED_SHA256)?;","handlingStrategy":"validation","validationCode":"let digest = file_sha256(&path)?;\nassert_eq!(digest, expected, \"artifact corrupted; re-download\");\nverify_sha256(&path, expected)?;","typeGuard":null,"tryCatchPattern":"match verify_sha256(&path, expected) {\n    Ok(()) => {}\n    Err(e) if e.kind() == io::ErrorKind::InvalidData => { /* re-download artifact */ }\n    Err(e) => return Err(e),\n}","preventionTips":["Download assets atomically and checksum before swapping into place","Pin checksums in one place and update them in the same commit as the asset version bump"],"tags":["checksum","integrity","download","rust"],"backgroundTag":"checksum-verification-failed","analyzedSha":"f457cff4f2648eee85d176f8a41861241d4e8428","analyzedAt":"2026-08-28T15:41:09.197Z","schemaVersion":2},"datasetVersion":"2026-08-28T16:17:29.566Z"}