BoundaryML/baml · error · io::Error
invalid sha256 blob digest; expected only hex characters
Error message
invalid sha256 blob digest; expected only hex characters
What it means
BlobRef::validate additionally requires every byte of the sha256 digest to be an ASCII hex digit (0-9, a-f, A-F). This error means the digest has the right length (64 chars) but contains non-hex characters, so it cannot be a valid SHA-256 representation. It is raised from normalized_digest via validate on any blob operation.
Source
Thrown at baml_language/crates/bex_events/src/value/artifact.rs:55
pub fn validate(&self) -> io::Result<()> {
if self.algorithm != Self::ALGORITHM_SHA256 {
return Err(io::Error::new(
io::ErrorKind::InvalidData,
format!("unsupported blob algorithm `{}`", self.algorithm),
));
}
if self.digest.len() != Self::SHA256_HEX_LEN {
return Err(io::Error::new(
io::ErrorKind::InvalidData,
format!(
"invalid sha256 blob digest length {}; expected {} hex characters",
self.digest.len(),
Self::SHA256_HEX_LEN
),
));
}
if !self.digest.bytes().all(|byte| byte.is_ascii_hexdigit()) {
return Err(io::Error::new(
io::ErrorKind::InvalidData,
"invalid sha256 blob digest; expected only hex characters",
));
}
Ok(())
}
fn normalized_digest(&self) -> io::Result<String> {
self.validate()?;
Ok(self.digest.to_ascii_lowercase())
}
fn verify_bytes(&self, bytes: &[u8]) -> io::Result<()> {
if bytes.len() != self.size_bytes {
return Err(io::Error::new(
io::ErrorKind::InvalidData,
format!(
"blob size mismatch for {}; expected {} bytes, got {} bytes",View on GitHub (pinned to bd85ce9dee)
Solutions
- Regenerate the digest using a hex encoder over the SHA-256 bytes (e.g. hex-encode the 32-byte hash).
- Strip any prefixes/decorations (0x, sha256:) from the digest string before storing it.
- If the digest is supposed to be binary-encoded, decode it properly instead of storing raw bytes as a string.
Example fix
// before let digest = String::from_utf8(sha256_bytes)?; // non-hex garbage // after let digest = hex::encode(sha256_bytes); // 64 ascii hex chars
Defensive patterns
Strategy: validation
Validate before calling
fn is_hex(s: &str) -> bool { !s.is_empty() && s.bytes().all(|b| b.is_ascii_hexdigit()) }
if digest.len() != 64 || !is_hex(&digest) { /* recompute from source bytes */ } Type guard
fn is_hex_digest(s: &str) -> bool { s.len() == 64 && s.bytes().all(|b| b.is_ascii_hexdigit()) } Try / catch
let digest = blob_ref.normalized_digest().map_err(|e| {
eprintln!("invalid blob digest: {e}");
e
})?; Prevention
- Hex-encode raw hash bytes with a dedicated hex encoder; never build digest strings from utf8 conversion.
- Strip prefixes like 'sha256:' or '0x' before storing digests.
- Centralize digest creation in one function so encoding is consistent.
When it happens
Trigger: A 64-character digest containing e.g. 'g'-'z', '0x' prefixes, padding characters, or binary garbage; encountered through normalized_digest(), write_blob(), read_blob(), or path_for().
Common situations: Digests produced by a custom hasher that emits base64, digests pasted from hex viewers with prefixes (0x), corrupted event files, or digests constructed via encoding mistakes (e.g. encoding raw bytes as utf8 instead of hex).
Understand the failure class
Background: "Invalid ... format", "must be in format X", "does not look like a ..." — invalid argument format errors across CLI tools and libraries — this error's family across 17 libraries.
Related errors
- invalid sha256 blob digest length {}; expected {} hex charac
- blob size mismatch for {}; expected {} bytes, got {} bytes
- blob digest mismatch for {}; computed {}
- dependency name `{name}` is reserved
- all ingress capacities and the response reservation must be
AI-assisted analysis of BoundaryML/baml@bd85ce9dee (2026-09-12).
Data as JSON: /api/errors/80d386f73f6af56f.
Report an issue: GitHub.