stalwartlabs/stalwart · error · DataCorruption

Invalid version

Error message

Invalid version

What it means

deserialize_events reads a binary-encoded event stream and expects the first byte to equal the current VERSION tag. If the stream ends immediately (handled separately) or the version byte differs, the data is treated as corrupt and deserialization bails with StoreEvent::DataCorruption.

Source

Thrown at crates/trc/src/serializers/binary.rs:38

) -> Vec<u8> {
    let mut buf = Vec::with_capacity(num_events * 64);
    buf.push(VERSION);
    leb128_write(&mut buf, num_events as u64);
    for event in events {
        event.serialize(&mut buf);
    }
    buf
}

pub fn deserialize_events(bytes: &[u8]) -> crate::Result<Vec<Event<EventDetails>>> {
    let mut iter = bytes.iter();
    if *iter.next().ok_or_else(|| {
        StoreEvent::DataCorruption
            .caused_by(crate::location!())
            .details("EOF while reading version")
    })? != VERSION
    {
        crate::bail!(
            StoreEvent::DataCorruption
                .caused_by(crate::location!())
                .details("Invalid version")
        );
    }
    let len = leb128_read(&mut iter).ok_or_else(|| {
        StoreEvent::DataCorruption
            .caused_by(crate::location!())
            .details("EOF while size")
    })? as usize;
    let mut events = Vec::with_capacity(len);
    for n in 0..len {
        events.push(Event::deserialize(&mut iter).ok_or_else(|| {
            StoreEvent::DataCorruption
                .caused_by(crate::location!())
                .details(format_compact!("Failed to deserialize event {n}"))
        })?);
    }

View on GitHub (pinned to e962003857)

Solutions

  1. Verify the data source actually contains trc binary-serialized events (not JSON/other logs).
  2. Check for version mismatch: decode with the same library version that produced the data.
  3. Restore the affected records/storage from backup if bytes are corrupted.
  4. Discard/re-serialize incompatible old payloads through a migration path if provided.

Example fix

// before
deserialize_events(&migrated_legacy_blob)
// after: only feed blobs produced by matching VERSION
deserialize_events(&record.payload)
Defensive patterns

Strategy: type-guard

Validate before calling

// before decoding, check the version byte
fn has_valid_version(buf: &[u8]) -> bool { buf.first() == Some(&VERSION) }

Type guard

fn is_binary_event(buf: &[u8]) -> bool { buf.first().copied() == Some(crate::serializers::binary::VERSION) }

Try / catch

match deserialize_events(&buf) {
    Ok(events) => events,
    Err(err) => { log::warn!("corrupt event stream: {err}"); Vec::new() },
}

Prevention

When it happens

Trigger: Feeding a byte buffer/iterator to deserialize_events whose first byte is not the serializer's VERSION constant — e.g. data written by a different format version, truncated/replaced header, or not binary-serialized data at all.

Common situations: Reading telemetry/event blobs persisted by an older or newer Stalwart release; corrupted storage records; pointing the code at a file/stream that isn't the expected binary format.

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.


AI-assisted analysis of stalwartlabs/stalwart@e962003857 (2026-09-06). Data as JSON: /api/errors/5914e321e0658279. Report an issue: GitHub.