astrid-runtime/astrid · critical

BLAKE3 evidence collision with inconsistent file lengths

Error message

BLAKE3 evidence collision with inconsistent file lengths

What it means

add_file_identity registers each whole-file BLAKE3 hash mapped to its byte length. If two different files hash to the same identity but have different lengths, this invariant is broken and the library bails, because BLAKE3 collisions with differing lengths indicate corrupted evidence or a hashing bug.

Solutions

  1. Verify the input file integrity (re-hash the source content and compare).
  2. Delete and regenerate the evidence/metrics state, since persisted identities are inconsistent.
  3. If reproducible with distinct files, report it — a genuine BLAKE3 collision with different lengths is a cryptographic anomaly.
Defensive patterns

Strategy: try-catch

Validate before calling

if let Some(prev) = known_lengths.get(&identity) {
    if *prev != file_len { return Err(anyhow!("inconsistent identity {}", identity)); }
}

Try / catch

match add_file_identity(&mut m, identity, len) {
    Ok(()) => {},
    Err(e) if e.to_string().contains("collision") => regenerate_evidence(),
    Err(e) => return Err(e),
}

Prevention

When it happens

Trigger: Calling add_file_identity (via measure or add_file) with a file whose BLAKE3 identity was already recorded under a different length — practically only via manually tampered identity data, truncated/mutated evidence stores, or a deliberately injected collision.

Common situations: Corrupted on-disk evidence files; hand-edited metrics inputs; fuzzing or adversarial tests feeding crafted collisions.

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 astrid-runtime/astrid@affd8760f4 (2026-09-09). Data as JSON: /api/errors/326992329f613e8a. Report an issue: GitHub.

Appendix: source

Thrown at crates/astrid-storage-chunker-evidence/src/metrics.rs:77

    cdc_chunk_lengths: Vec<(u64, u64)>,
}

impl Accumulator {
    pub fn add_file(&mut self, bytes: &[u8]) -> Result<()> {
        let length = u64::try_from(bytes.len())?;
        self.add_file_identity(length, *blake3::hash(bytes).as_bytes())
    }

    pub fn add_file_identity(&mut self, length: u64, identity: [u8; 32]) -> Result<()> {
        self.files = checked_add(self.files, 1, "file count")?;
        self.logical_bytes = checked_add(self.logical_bytes, length, "logical byte count")?;
        match self.whole_files.entry(identity) {
            std::collections::hash_map::Entry::Vacant(entry) => {
                entry.insert(length);
            },
            std::collections::hash_map::Entry::Occupied(entry) => {
                if *entry.get() != length {
                    bail!("BLAKE3 evidence collision with inconsistent file lengths");
                }
            },
        }
        Ok(())
    }

    pub fn add_whole_record(&mut self, bytes: &[u8]) -> Result<()> {
        self.add_record(bytes, 1, false)
    }

    pub fn add_chunk_record(&mut self, bytes: &[u8], logical_chunks: u64) -> Result<()> {
        self.add_record(bytes, logical_chunks, true)
    }

    fn add_record(&mut self, bytes: &[u8], logical_chunks: u64, cdc: bool) -> Result<()> {
        if logical_chunks == 0 {
            bail!("a representation record must cover at least one logical chunk");
        }

View on GitHub (pinned to affd8760f4)