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
- Dump the colliding pair and verify record b against the WriteRef encoding.
- Drop or regenerate the affected backup segment and re-run the subcompaction.
- Audit the producer (import/backup tool version) for format mismatches.
- 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
- Keep CF_WRITE populated only with WriteRef-encoded values.
- Regenerate damaged log-backup segments instead of compacting them.
- Verify checksums on backup data before compaction.
- Sync writer tool versions with the compactor's expected format.
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
- record a isn't a valid write
- cannot resolve the conflict {} for key {}
- failed to load_latest_options {:?}
- ime cross check fail(parse error);
- ime cross check fail(parse error);
AI-assisted analysis of tikv/tikv@78aedc1c81 (2026-09-03).
Data as JSON: /api/errors/82ed4d0a2ef291b9.
Report an issue: GitHub.