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
- Re-write the entry so the version tag matches the field count (V0 -> 3 fields, V1 -> 4 fields).
- Fix the encoder to emit exactly 4 entries when writing V1 records.
- 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
- Emit all four V1 fields (including the timestamp) when writing V1 records
- Do not label a 3-field payload as V1; that is a V0 record
- Write encode/decode round-trip tests per record version
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
- too many entries in v0 kv record
- too many entries in v0 dotfiles var delete record
- too many entries in v0 script record
- too many entries in v0 shell alias create record
- too many entries in v0 shell alias delete record
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)