atuinsh/atuin · error

daemon sent an unspecified history tail event

Error message

daemon sent an unspecified history tail event

What it means

from_proto requires every tail event to carry a concrete event kind (Started, Ended, Cancelled, or Lagged). A TailHistoryReply with a None event means the daemon sent a message with no event payload set, which is outside the wire contract, so conversion fails.

Source

Thrown at crates/atuin/src/command/client/history.rs:704

    #[serde(skip_serializing_if = "Option::is_none")]
    duration: Option<String>,
    #[serde(skip_serializing_if = "Option::is_none")]
    success: Option<bool>,
    #[serde(skip_serializing_if = "Option::is_none")]
    finished_at: Option<String>,
}

#[cfg(feature = "daemon")]
impl TailEvent {
    fn from_proto(reply: TailHistoryReply) -> Result<Self> {
        let (kind, history) = match reply.event {
            Some(TailEventProto::Started(history)) => (TailKind::Started, history),
            Some(TailEventProto::Ended(history)) => (TailKind::Ended, history),
            Some(TailEventProto::Cancelled(history)) => (TailKind::Cancelled, history),
            Some(TailEventProto::Lagged(_)) => {
                bail!("daemon sent a lag notice as a history event")
            }
            None => bail!("daemon sent an unspecified history tail event"),
        };
        let timestamp = OffsetDateTime::from_unix_nanos_i64(history.timestamp);
        let author_kind = history.author_kind();

        Ok(Self {
            kind,
            history: History {
                id: history
                    .id
                    .ok_or_else(|| eyre::eyre!("daemon tail event is missing the history id"))?
                    .try_into()?,
                timestamp,
                duration: history.duration,
                exit: history.exit,
                command: history.command,
                cwd: history.cwd,
                session: history.session,
                #[allow(deprecated)]

View on GitHub (pinned to c0c717ab04)

Solutions

  1. Restart the daemon to ensure matching protocol versions
  2. Upgrade CLI and daemon to the same atuin release
  3. File a bug if reproducible on matched versions
Defensive patterns

Strategy: try-catch

Try / catch

match TailHistoryReply::from_proto(reply) {
    Err(e) if e.to_string().contains("unspecified history tail event") => {
        // skip/drop the frame and reconnect the tail stream
    }
    other => other?,
}

Prevention

When it happens

Trigger: Daemon sends a TailHistoryReply where reply.event is None (the oneof is unset) while the client decodes it as a history event.

Common situations: Protocol version skew or daemon bug producing empty oneof fields; interrupted/degraded stream frames from an old daemon.

Related errors


AI-assisted analysis of atuinsh/atuin@c0c717ab04 (2026-09-12). Data as JSON: /api/errors/95a176b73c00d4c8. Report an issue: GitHub.