atuinsh/atuin · error

too many entries in v1 kv record

Error message

too many entries in v1 kv record

What it means

A V1 KV record must be a MsgPack array of exactly 4 entries (namespace, key, value, plus the V1-added field such as a timestamp). The deserializer enforces nfields == 4 for version V1 and rejects any other count to keep the wire format strict.

Solutions

  1. Re-write the entry so the version tag matches the field count (V0 -> 3 fields, V1 -> 4 fields).
  2. Fix the encoder to emit exactly 4 entries when writing V1 records.
  3. Upgrade older clients that cannot read V1 records to a compatible Atuin version.

Example fix

// before: tagged V1 but encoded as (namespace, key, value)
// after:  encode::to_bytes(&(namespace, key, value, timestamp)) with RecordVersion::V1
Defensive patterns

Strategy: validation

Validate before calling

let value: rmpv::Value = rmpv::decode::read_value(&mut cursor)?;
// v1 kv record requires exactly 4 fields
assert_eq!(value.as_array().map(|a| a.len()), Some(4));

Type guard

fn is_well_formed_v1_kv(payload: &[u8]) -> bool {
    rmpv::decode::read_value(&mut std::io::Cursor::new(payload))
        .ok()
        .and_then(|v| v.as_array().map(|a| a.len() == 4))
        .unwrap_or(false)
}

Try / catch

match KvRecord::deserialize(&data) {
    Ok(rec) => apply(rec),
    Err(e) if e.to_string().contains("too many entries in v1") => {
        log::warn("v1 kv record field-count mismatch: {e}");
    }
    Err(e) => return Err(e.into()),
}

Prevention

When it happens

Trigger: Deserializing a record tagged V1 whose array length != 4 — e.g. a V0 payload mislabeled as V1, an encoder omitting the fourth field, or one adding extra fields.

Common situations: Mixed-version KV clients in a sync mesh, manual record construction with the wrong version tag, or corruption during storage/sync.

Understand the failure class

Background: Schema validation failed / invalid input schema: payload rejected because its shape doesn't match the expected schema — this error's family across 28 libraries.

Related errors


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

Appendix: source

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

                    decode::read_str_from_slice(bytes).map_err(error_report)?;
                let (key, bytes) = decode::read_str_from_slice(bytes).map_err(error_report)?;
                let (value, bytes) = decode::read_str_from_slice(bytes).map_err(error_report)?;

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

                Ok(Self {
                    namespace: namespace.to_owned(),
                    key: key.to_owned(),
                    value: Some(value.to_owned()),
                })
            }
            RecordVersion::V1 => {
                let mut bytes = decode::Bytes::new(&data.0);

                let nfields = decode::read_array_len(&mut bytes).map_err(error_report)?;
                ensure!(nfields == 4, "too many entries in v1 kv record");

                let bytes = bytes.remaining_slice();

                let (namespace, bytes) =
                    decode::read_str_from_slice(bytes).map_err(error_report)?;
                let (key, mut bytes) = decode::read_str_from_slice(bytes).map_err(error_report)?;
                let has_value = decode::read_bool(&mut bytes).map_err(error_report)?;

                let (value, bytes) = if has_value {
                    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");

View on GitHub (pinned to 900007c480)