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

  1. Regenerate the digest using a hex encoder over the SHA-256 bytes (e.g. hex-encode the 32-byte hash).
  2. Strip any prefixes/decorations (0x, sha256:) from the digest string before storing it.
  3. 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

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


AI-assisted analysis of BoundaryML/baml@bd85ce9dee (2026-09-12). Data as JSON: /api/errors/80d386f73f6af56f. Report an issue: GitHub.