atuinsh/atuin · error

{reason}. Enable `daemon.autostart = true` or restart the da

Error message

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

What it means

ready_client probes the daemon for readiness and returns a ready HistoryClient only if version/protocol match. When the probe reports NeedsRestart but autostart is disabled, the client cannot recover on its own and bails with the same remediation hint. It is the top-level guard used before issuing daemon RPCs.

Source

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

    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),
        Probe::Unreachable(err) if !should_retry_after_error(&err) => return Err(err),
        Probe::NeedsRestart(_) | Probe::Unreachable(_) => {}
    }

    restart_daemon(settings).await
}

/// Send a request to the daemon, first ensuring (via [`ready_client`]) that it is running and
/// speaks our version.
async fn try_with_restart<C, F, R>(settings: &Settings, send_request: F, context: C) -> Result<R>
where
    F: AsyncFn(&mut HistoryClient, C) -> Result<R> + Sync,
    R: atuin_daemon::grpc::VersionedReply,

View on GitHub (pinned to c0c717ab04)

Solutions

  1. Restart the daemon manually so version/protocol match the CLI
  2. Enable daemon.autostart = true in the Atuin config so the client restarts skewed daemons automatically
  3. Ensure only one atuin installation provides the daemon

Example fix

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

Strategy: try-catch

Validate before calling

let cfg: toml::Value = toml::from_str(&std::fs::read_to_string("~/.config/atuin/config.toml")?)?;
if !cfg["daemon"]["autostart"].as_bool().unwrap_or(false) {
    eprintln!("autostart disabled: ensure the running daemon matches this CLI version");
}

Try / catch

match ready_client(&settings).await {
    Err(e) if e.to_string().contains("Enable `daemon.autostart = true`") => {
        // restart daemon and retry
    }
    other => other?,
}

Prevention

When it happens

Trigger: probe() returns Probe::NeedsRestart(reason) (version or protocol skew) while settings.daemon.autostart is false; called from try_with_restart during any daemon-backed command.

Common situations: Same as error 80: stale daemon after an upgrade, mixed installations, autostart disabled in config.

Related errors


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