[Bug]: daemon `autostart` will not respawn a wedged (alive-but-not-serving) daemon — a live pidfile blocks recovery, freezing every shell command
Version
atuin 18.22.0 (Linux, [daemon] enabled = true, autostart = true).
Summary
autostart decides whether to spawn the daemon from the pidfile alone: if the recorded
PID is alive, it assumes a healthy daemon and does nothing. But a daemon process can be alive
yet not serving its socket (the socket was removed out from under it, or it stopped listening).
In that state autostart never respawns a working daemon, so every interactive command's
preexec (atuin history start) keeps hitting a dead/absent socket and blocking — permanently,
until manual intervention, not transiently.
This is adjacent to #3382 (client freezes on a non-serving socket), but the root here is
specifically that autostart's liveness check is "is the PID alive?" instead of "is the daemon
actually reachable?", so the system can't self-heal.
What I observed
- The daemon PID (recorded in
~/.local/share/atuin/atuin-daemon.pid) was alive, but held no listening socket —ss -xshowed none, and no socket file existed at the expected path. - Every command stalled ~4 s in
atuin history start.autostartnever respawned the daemon because the pidfile PID was still alive. - Killing the wedged PID (which made the pidfile stale) let
autostartspawn a fresh, healthy daemon on the nextatuininvocation, and per-command latency dropped from ~4 s back to ~0.03 s.
Repro
[daemon] enabled = true, autostart = true; let the daemon start.- Put it into an alive-but-not-serving state — e.g.
rmits socket file while the process keeps running (in my real-world case a scratch-dir cleaner removed the socket), or otherwise make it stop listening while the PID stays up. - Run any command.
atuin history startinpreexecblocks;autostartdoes not respawn a working daemon because the pidfile PID is still alive. The stall persists across every command.
Suggested fix
autostart's health check should confirm the daemon is actually serving (connect to the
socket / do a lightweight handshake), not merely that the pidfile PID is alive. If the PID is
alive but unreachable, respawn it (or clear the stale pidfile and start fresh) — or at minimum
surface an error — instead of assuming healthy. Pairs well with the short client-side connect
timeout requested in #3382: a fast timeout stops the freeze, and a serving-not-just-alive check
lets the daemon recover on its own.
Workaround
Run the daemon under an external supervisor with autostart = false — e.g. a systemd --user
unit with Restart=always and an ExecStartPre that clears a stale socket — so recovery does
not depend on autostart's pidfile check.
Source: atuinsh/atuin