herdrdev/herdr · error · io::Error
sha256 mismatch: expected {expected}, got {actual}
Error message
sha256 mismatch: expected {expected}, got {actual} What it means
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.
Source
Thrown at src/checksum.rs:20
fs::File,
io::{self, Read},
path::Path,
};
use sha2::{Digest, Sha256};
pub(crate) fn verify_sha256(path: &Path, expected: &str) -> io::Result<()> {
let expected = expected.trim().to_ascii_lowercase();
if expected.len() != 64 || !expected.chars().all(|ch| ch.is_ascii_hexdigit()) {
return Err(io::Error::new(
io::ErrorKind::InvalidData,
"expected sha256 must be 64 hexadecimal characters",
));
}
let actual = file_sha256(path)?;
if actual != expected {
return Err(io::Error::new(
io::ErrorKind::InvalidData,
format!("sha256 mismatch: expected {expected}, got {actual}"),
));
}
Ok(())
}
fn file_sha256(path: &Path) -> io::Result<String> {
let mut file = File::open(path)?;
let mut hasher = Sha256::new();
let mut buffer = [0u8; 64 * 1024];
loop {
let read = file.read(&mut buffer)?;
if read == 0 {
break;
}
hasher.update(&buffer[..read]);
}View on GitHub (pinned to f457cff4f2)
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
Example fix
// before verify_sha256(&bundle_path, PINNED_SHA256)?; // after // regenerate the pin after intentionally updating the artifact const PINNED_SHA256: &str = "<new 64-hex digest>"; verify_sha256(&bundle_path, PINNED_SHA256)?;
Defensive patterns
Strategy: validation
Validate before calling
let digest = file_sha256(&path)?; assert_eq!(digest, expected, "artifact corrupted; re-download"); verify_sha256(&path, expected)?;
Try / catch
match verify_sha256(&path, expected) {
Ok(()) => {}
Err(e) if e.kind() == io::ErrorKind::InvalidData => { /* re-download artifact */ }
Err(e) => return Err(e),
} Prevention
- 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
When it happens
Trigger: 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.
Common situations: Interrupted update downloads, CDN/proxy corruption, stale checksum constants after bumping an asset version, or manually patched vendor files.
Related errors
- downloaded remote asset checksum verification failed: {err}
- expected sha256 must be 64 hexadecimal characters
- download failed: {err}
- remote bridge download failed: {err}
- api request line is too large
AI-assisted analysis of herdrdev/herdr@f457cff4f2 (2026-08-28).
Data as JSON: /api/errors/d0313d50695c48f8.
Report an issue: GitHub.