atuinsh/atuin · error

unknown kv record version

Error message

unknown kv record version {other:?}

What it means

The KV record decoder reads a leading version byte and implements only known versions. Any other version triggers 'unknown kv record version'. This protects against decoding payloads whose binary format changed between releases.

Solutions

  1. Upgrade atuin-kv / atuin to a version supporting the record's version byte
  2. Skip unknown-version records instead of failing the entire sync
  3. Re-create the KV data with the current client
Defensive patterns

Strategy: try-catch

Validate before calling

// Check the KV record version byte before decoding
// fn is_supported_kv_version(payload: &[u8]) -> bool {
//     payload.first() == Some(&KV_KNOWN_VERSION)
// }

Type guard

fn is_supported_kv_version(payload: &[u8]) -> bool {
    payload.first() == Some(&KV_KNOWN_VERSION)
}

Try / catch

match KvRecord::deserialize(&mut bytes) {
    Ok(rec) => apply(rec),
    Err(e) if e.to_string().contains("unknown kv record version") => {
        log::warn!("kv record from newer atuin; skipping until upgrade");
    }
    Err(e) => return Err(e),
}

Prevention

When it happens

Trigger: Deserializing a KV record whose version byte is not one compiled into this build — commonly newer-format records synced down to an older client.

Common situations: Version skew between synced machines; restoring old records into a newer store or vice versa.

Related errors


AI-assisted analysis of atuinsh/atuin@900007c480 (2026-09-19). Data as JSON: /api/errors/c4fa552d44a1e4bb. Report an issue: GitHub.

Appendix: source

Thrown at crates/atuin-kv/src/store/record.rs:97

                    let (value, bytes) =
                        decode::read_str_from_slice(bytes).map_err(error_report)?;
                    (Some(value.to_owned()), bytes)
                } else {
                    (None, bytes)
                };

                if !bytes.is_empty() {
                    bail!("trailing bytes in encoded kvrecord. malformed");
                }

                Ok(Self {
                    namespace: namespace.to_owned(),
                    key: key.to_owned(),
                    value,
                })
            }
            other => {
                bail!("unknown kv record version {other:?}");
            }
        }
    }
}

#[cfg(test)]
mod tests {
    use atuin_domain::record::RecordVersion;
    use rstest::rstest;

    use super::{DecryptedData, KvRecord};

    #[rstest]
    #[case::some(
        Some("baz".to_owned()),
        &[0x94, 0xa3, b'f', b'o', b'o', 0xa3, b'b', b'a', b'r', 0xc3, 0xa3, b'b', b'a', b'z']
    )]
    #[case::none(None, &[0x94, 0xa3, b'f', b'o', b'o', 0xa3, b'b', b'a', b'r', 0xc2])]

View on GitHub (pinned to 900007c480)