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 is a msgpack array of exactly 4 fields (namespace, key, and two additional fields introduced in v1). The deserializer rejects any v1 record whose decoded array length is not 4, indicating the payload does not match the v1 schema. This prevents misreading corrupt or older-format data as a valid v1 record.

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 c0c717ab04)

Solutions

  1. Upgrade atuin on all synced machines to identical versions
  2. Delete the malformed v1 record and re-set the kv pair
  3. Fall back/re-sync the kv store from a healthy host
  4. If a pre-release client produced the record, identify and remove it from sync
Defensive patterns

Strategy: validation

Validate before calling

let n = rmp::decode::read_array_len(&mut bytes)?;
if n != 4 { return Err("malformed v1 kv record".into()); }

Try / catch

match KvRecord::deserialize(RecordVersion::V1, &data) {
    Ok(rec) => apply(rec),
    Err(e) => log::warn!("skipping malformed v1 kv record: {e}"),
}

Prevention

When it happens

Trigger: Deserializing a RecordVersion::V1 kv record whose msgpack payload decodes to an array length other than 4.

Common situations: Version skew where an old or newer client writes v1 records with a different shape; corrupted sync data; manual DB edits.

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@c0c717ab04 (2026-09-12). Data as JSON: /api/errors/807013849b8f48e1. Report an issue: GitHub.