atuinsh/atuin · error
id should be 16 bytes
Error message
id should be 16 bytes
What it means
The daemon search engine streams results from the background atuin daemon over gRPC; each id is a 16-byte UUID in wire form. The client converts each id with id.as_slice().try_into::<[u8; 16]>() and expects success. A response carrying an id whose byte length differs (short, long, corrupt) violates the protocol and panics with 'id should be 16 bytes' inside the search flow.
Source
Thrown at crates/atuin/src/command/client/search/engines/daemon.rs:199
let mut ids = Vec::with_capacity(200);
span!(Level::TRACE, "daemon_search.resp")
.in_scope(async || -> Result<()> {
while let Some(response) = stream.message().await? {
let span2 = span!(
Level::TRACE,
"daemon_search.resp.item",
query_id = response.query_id
);
let span2_guard = span2.enter();
// Only process if the query_id matches (prevents stale responses)
if response.query_id == query_id {
let uuids = response
.ids
.iter()
.map(|id| {
let bytes: [u8; 16] =
id.as_slice().try_into().expect("id should be 16 bytes");
Uuid::from_bytes(bytes).as_simple().to_string()
})
.collect::<Vec<_>>();
ids.extend(uuids);
}
drop(span2_guard);
drop(span2);
}
Ok(())
})
.await?;
drop(span);
if ids.is_empty() {
debug!(query = %query, results = 0, "[daemon-client] empty results");
return Ok(Vec::new());
}
View on GitHub (pinned to 202f6ad98e)
Solutions
- Restart the daemon so client and daemon run the same atuin version (kill the stale process; it autostarts on next search)
- Verify client and daemon come from the same install: compare atuin --version with the daemon's version/logs
- If it persists, report it with daemon logs — a non-16-byte id is a protocol violation bug
Example fix
// before
let bytes: [u8; 16] = id.as_slice().try_into().expect("id should be 16 bytes");
// after — skip malformed ids instead of crashing the TUI
let Ok(bytes) = <[u8; 16]>::try_from(id.as_slice()) else {
tracing::warn!(len = id.len(), "daemon sent a non-16-byte id; skipping");
continue;
}; Defensive patterns
Strategy: validation
Validate before calling
// drop malformed ids before conversion
let uuids: Vec<_> = response
.ids
.iter()
.filter(|id| id.len() == 16)
.map(|id| Uuid::from_bytes(id.as_slice().try_into().unwrap()))
.collect(); Type guard
fn is_uuid_bytes(id: &[u8]) -> bool {
id.len() == 16
} Try / catch
// isolate per-item conversion so one bad id fails a single search, not the TUI
let uuids: Vec<_> = response.ids.iter().filter_map(|id| {
<[u8; 16]>::try_from(id.as_slice())
.ok()
.map(|b| Uuid::from_bytes(b).as_simple().to_string())
}).collect(); Prevention
- Restart the daemon after every atuin upgrade so both sides speak the same protocol
- Pin client and daemon to the same version in deployments
- Watch daemon logs for malformed SearchResponse messages after upgrades
When it happens
Trigger: full_query() processing a SearchResponse whose ids contain anything other than exactly 16 bytes: a client/daemon version mismatch that changes the id encoding, a truncated or corrupted gRPC message, or a daemon bug emitting malformed ids.
Common situations: Upgrading the atuin CLI while the old daemon keeps running (client and daemon from different versions); a stale daemon socket after a partial upgrade; protocol drift between atuin components.
Related errors
- invalid id UUID format in sqlite DB
- failed to parse uuid for local store status
- failed to register sigterm handler
- failed to register sigint handler
- failed to listen for ctrl+c
AI-assisted analysis of atuinsh/atuin@202f6ad98e (2026-08-16).
Data as JSON: /api/errors/cf5d8f17a8d1db0e.
Report an issue: GitHub.