tikv/tikv · critical

record b isn't a valid write

Error message

record b isn't a valid write

What it means

Same resolve() collision path as the 'record a' panic, but for the second colliding value: WriteRef::parse failed to decode record b as a write-CF entry. Resolution needs both writes' write_type/start_ts to pick a winner, so a malformed record b is fatal.

Source

Thrown at components/compact-log-backup/src/compaction/exec.rs:157

    /// In any case out of knowledge.
    fn resolve(c: &Subcompaction, a: &mut Record, b: &mut Record) {
        if a == b {
            return;
        }

        let cid = rand::random::<u64>();
        use util::redact;
        warn!("encountering two different values: try to resolve them."; "key" => redact(&a.key), 
            "value_a" => redact(&a.value), "value_b" => redact(&b.value), "conflict_id" => cid);

        if c.cf != CF_WRITE {
            panic!(
                "encountering two different values but they are not in write CF, it is {}; cid = {}.",
                c.cf, cid
            );
        }
        let wa = WriteRef::parse(&a.value).expect("record a isn't a valid write");
        let wb = WriteRef::parse(&b.value).expect("record b isn't a valid write");

        let sanity_check = |wa: &WriteRef<'_>, wb: &WriteRef<'_>| {
            if wa.write_type == wb.write_type {
                assert_eq!(wa.start_ts, wb.start_ts);
            }
            // This cannot be applied to `Rollback` because `protected` was encoded to
            // `short_value`.
            if wa.write_type == WriteType::Put && wb.write_type == WriteType::Put {
                assert_eq!(wa.short_value, wb.short_value);
            }
        };
        // partial ordering of two conflicting records.
        let partial_cmp = |wa: &WriteRef<'_>, wb: &WriteRef<'_>| {
            use WriteType::*;
            match (wa.write_type, wb.write_type) {
                // Rollback -> Collapsed with Put happens.
                // Should keep the Put.
                (Put, Rollback | Put) if wa.has_overlapped_rollback => Some(Ordering::Greater),

View on GitHub (pinned to 78aedc1c81)

Solutions

  1. Dump the colliding pair and verify record b against the WriteRef encoding.
  2. Drop or regenerate the affected backup segment and re-run the subcompaction.
  3. Audit the producer (import/backup tool version) for format mismatches.
  4. Repair or skip the bad record only after confirming no other consumers depend on it.

Example fix

// before
let wb = WriteRef::parse(&b.value).expect("record b isn't a valid write");
// after
let wb = WriteRef::parse(&b.value)
    .unwrap_or_else(|e| panic!("record b at key {:?} is not a valid write: {:?}", b.key, e));
Defensive patterns

Strategy: validation

Validate before calling

// pre-validate both sides of a collision before resolve()
assert!(WriteRef::parse(&a.value).is_ok(), "record a invalid");
assert!(WriteRef::parse(&b.value).is_ok(), "record b invalid");

Type guard

fn valid_write_ref(v: &[u8]) -> Option<WriteRef<'_>> {
    WriteRef::parse(v).ok()
}

Prevention

When it happens

Trigger: resolve() on two values for the same key where record b in CF_WRITE is truncated, has an invalid write-type byte, or was written by incompatible code.

Common situations: Damaged log-backup artifacts, import tools writing bad values into CF_WRITE, or version drift between data writers and the compactor.

Related errors


AI-assisted analysis of tikv/tikv@78aedc1c81 (2026-09-03). Data as JSON: /api/errors/82ed4d0a2ef291b9. Report an issue: GitHub.