atuinsh/atuin · error
{message}
Error message
{message} What it means
Before using a running daemon, the CLI checks that the daemon's reported version and protocol are compatible with the client (`ensure_reply_compatible`). On mismatch it builds a descriptive message: if autostart is enabled the daemon can simply be restarted transparently, so it bails with the plain message (the caller `try_with_restart` will handle restart); otherwise it appends guidance to enable autostart or restart manually.
Solutions
- Kill the running daemon (`pkill atuin` / stop the systemd unit) and let the new binary start it.
- Enable `daemon.autostart = true` in config so the CLI restarts mismatched daemons automatically.
- Re-run the command after restarting so both sides match versions.
- Ensure only one installation of atuin is on PATH (`which -a atuin`) and upgrade all of them consistently.
Example fix
# before atuin stats # daemon is old version -> version/protocol mismatch bail # after pkill atuin && atuin stats # restart daemon with the upgraded client
Defensive patterns
Strategy: retry
Validate before calling
# compare running daemon version with the CLI atuin version 2>/dev/null; pgrep -a atuin # stale daemon process?
Try / catch
match client_cmd() {
Err(e) if e.to_string().contains("Enable `daemon.autostart`") || e.to_string().contains("protocol") => {
stop_daemon();
client_cmd() // retry; autostart spawns a matching daemon
}
other => other,
} Prevention
- Restart the daemon after every atuin upgrade.
- Enable daemon.autostart so mismatched daemons are replaced automatically.
- Keep a single atuin installation on PATH.
- In containers, run client and daemon from the same image/version.
When it happens
Trigger: Client and daemon binaries of different atuin versions are mixed (e.g. after upgrading the CLI but the old daemon process is still running), or the daemon speaks a different protocol version than the client expects.
Common situations: Upgrading atuin (package manager, cargo install) while a long-lived daemon keeps running the old build; switching between system and self-built binaries; running the CLI in a container against a host daemon.
Related errors
- daemon sent a lag notice as a history event
- daemon sent an unspecified history tail event
- `atuin history tail` requires Atuin to be built with the…
- Could not login due to version mismatch
- could not register user due to version mismatch
AI-assisted analysis of atuinsh/atuin@900007c480 (2026-09-19).
Data as JSON: /api/errors/5cb3708f7311cb2a.
Report an issue: GitHub.
Appendix: source
Thrown at crates/atuin/src/command/client/daemon.rs:425
let _ = wait_until_ready(settings, timeout).await?;
drop(startup_lock);
Ok(())
}
async fn restart_daemon(settings: &Settings) -> Result<HistoryClient> {
ensure_daemon_running(settings).await?;
connect_client(settings).await
}
fn ensure_reply_compatible(settings: &Settings, version: &str, protocol: u32) -> Result<()> {
if daemon_matches_expected(version, protocol) {
return Ok(());
}
let message = daemon_mismatch_message(version, protocol);
if settings.daemon.autostart {
bail!("{message}");
}
bail!("{message}. Enable `daemon.autostart = true` or restart the daemon manually");
}
/// Acquire a [`HistoryClient`] connected to a daemon we expect to understand us, probing over the
/// wire-stable Status RPC first and restarting the daemon if it is absent or version/protocol
/// skewed.
///
/// TODO(markovejnovic): This is egregious slop, but not worse than the original solution. I want to
/// remove this in a future PR: <https://github.com/atuinsh/atuin/pull/4002>
pub async fn ready_client(settings: &Settings) -> Result<HistoryClient> {
match probe(settings).await {
Probe::Ready(client) => return Ok(client),
Probe::NeedsRestart(reason) if !settings.daemon.autostart => {
bail!("{reason}. Enable `daemon.autostart = true` or restart the daemon manually");
}
Probe::Unreachable(err) if is_legacy_daemon_error(&err) => {View on GitHub (pinned to 900007c480)