atuinsh/atuin · error

{message}. Enable `daemon.autostart = true` or restart the d

Error message

{message}. Enable `daemon.autostart = true` or restart the daemon manually

What it means

When the running daemon's version or wire protocol does not match what the CLI expects, ensure_reply_compatible builds a mismatch message and bails. If daemon autostart is disabled, the CLI cannot restart the daemon itself, so it appends advice to either enable autostart or restart manually. This protects the client from talking to a daemon it cannot interoperate with.

Source

Thrown at crates/atuin/src/command/client/daemon.rs:429

    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) => {
            return Err(err.wrap_err(LEGACY_DAEMON_RESTART_MESSAGE));
        }
        Probe::Unreachable(err) if !settings.daemon.autostart => return Err(err),

View on GitHub (pinned to c0c717ab04)

Solutions

  1. Restart the atuin daemon manually (atuin daemon stop && atuin daemon start) so it matches the CLI version
  2. Set daemon.autostart = true in ~/.config/atuin/config.toml so the CLI can restart a mismatched daemon automatically
  3. Kill the stale daemon process and rerun the command

Example fix

// before (config.toml)
[daemon]
autostart = false
// after
[daemon]
autostart = true
Defensive patterns

Strategy: try-catch

Validate before calling

// check config before running daemon-backed commands
let cfg: toml::Value = toml::from_str(&std::fs::read_to_string("~/.config/atuin/config.toml")?)?;
let autostart = cfg.get("daemon").and_then(|d| d.get("autostart")).and_then(|v| v.as_bool()).unwrap_or(false);
if !autostart { eprintln!("daemon.autostart is disabled; restart the daemon manually after upgrades"); }

Try / catch

match cmd.await {
    Err(e) if e.to_string().contains("Enable `daemon.autostart = true`") => {
        // restart daemon manually, then retry once
    }
    other => other?,
}

Prevention

When it happens

Trigger: The CLI connects to a daemon whose reported version or protocol differs from the CLI's own, and settings.daemon.autostart is false, so try_with_restart cannot recover by restarting the daemon.

Common situations: Upgrading the atuin binary while an old daemon process is still running; daemons started from a different install (distro package vs cargo install); autostart explicitly disabled in config.

Related errors


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