BoundaryML/baml · error · io::Error

profiling usage ledger checksum mismatch

Error message

profiling usage ledger checksum mismatch

What it means

The usage ledger stores its value plus a SHA-256 checksum over the first 16 bytes (magic + 8-byte big-endian counter). read_usage_state recomputes the digest and compares it to bytes[16..]; a mismatch raises InvalidData 'profiling usage ledger checksum mismatch', meaning the ledger content was altered or corrupted after it was written.

Solutions

  1. Delete usage.state so the store rebuilds the ledger from the actual on-disk segment sizes.
  2. Restore usage.state from a known-good backup.
  3. Check storage health (dmesg/SMART) if corruption recurs; run the store on reliable media.
  4. Do not hand-edit the ledger — always update usage through the store's own APIs so the checksum stays consistent.

Example fix

// shell
// before: usage.state checksum mismatch (edited counter)
// after
rm <store-root>/usage.state  # ledger is recomputed from segments on next open
Defensive patterns

Strategy: validation

Validate before calling

let bytes = fs::read(root.join("usage.state"))?;
let expected: [u8; 32] = Sha256::digest(&bytes[..16]).into();
if bytes.len() == 48 && bytes[16..] != expected {
    // checksum mismatch: delete the ledger so it is rebuilt from segments
}

Try / catch

match read_usage_state(&root) {
    Ok(usage) => usage,
    Err(e) if e.to_string().contains("checksum mismatch") => {
        let _ = fs::remove_file(root.join("usage.state"));
        rebuild_usage_from_segments(&root)
    }
    Err(e) => return Err(e.into()),
}

Prevention

When it happens

Trigger: Reading usage.state whose first 16 bytes (magic+counter) no longer match the stored 32-byte SHA-256 digest — e.g. hand-edited counter bytes, torn write, or bit rot in the first 16 bytes.

Common situations: Someone tried to tamper with the recorded usage counter; a crash/power loss during the ledger write left mismatched bytes; disk corruption; partial copy of the file.

Understand the failure class

Background: Checksum mismatch errors: "checksum verification failed", "digest mismatch", "expected vs actual checksum" — what they mean and how to fix them — this error's family across 41 libraries.

Related errors


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

Appendix: source

Thrown at baml_language/crates/bex_prof_store/src/prof/backend/store.rs:1664

    let mut total = USAGE_STATE_BYTES;
    scan(root, root, &mut total)?;
    Ok(total)
}

fn read_usage_state(root: &Path) -> io::Result<u64> {
    let usage_state_len = usize::try_from(USAGE_STATE_BYTES).expect("fixed usage state fits usize");
    let mut bytes = Vec::with_capacity(usage_state_len);
    File::open(root.join("usage.state"))?.read_to_end(&mut bytes)?;
    if bytes.len() != usage_state_len || &bytes[..8] != USAGE_MAGIC {
        return Err(io::Error::new(
            io::ErrorKind::InvalidData,
            "invalid profiling usage ledger",
        ));
    }
    let expected: [u8; 32] = Sha256::digest(&bytes[..16]).into();
    if bytes[16..] != expected {
        return Err(io::Error::new(
            io::ErrorKind::InvalidData,
            "profiling usage ledger checksum mismatch",
        ));
    }
    Ok(u64::from_be_bytes(
        bytes[8..16].try_into().expect("fixed-width usage"),
    ))
}

fn write_usage_state(root: &Path, usage: u64, platform: &dyn StorePlatform) -> io::Result<()> {
    let usage_state_len = usize::try_from(USAGE_STATE_BYTES).expect("fixed usage state fits usize");
    let mut bytes = Vec::with_capacity(usage_state_len);
    bytes.extend_from_slice(USAGE_MAGIC);
    bytes.extend_from_slice(&usage.to_be_bytes());
    let checksum: [u8; 32] = Sha256::digest(&bytes).into();
    bytes.extend_from_slice(&checksum);
    let temporary = root.join("tmp/usage-state.pending");
    let mut file = OpenOptions::new()

View on GitHub (pinned to bd85ce9dee)