atuinsh/atuin · error

daemon already running (pidfile lock busy at {})

Error message

daemon already running (pidfile lock busy at {})

What it means

The daemon uses an flock on a pidfile as a single-instance lock. acquire() tries a non-blocking lock; if it returns WouldBlock, another daemon process holds the lock, so this invocation aborts with the pidfile path in the message. It is the daemon's mutual-exclusion mechanism, not a filesystem permission problem.

Source

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

const DAEMON_VERSION: &str = env!("CARGO_PKG_VERSION");
const DAEMON_PROTOCOL_VERSION: u32 = 2;
const STARTUP_POLL: Duration = Duration::from_millis(40);
const LOCK_POLL: Duration = Duration::from_millis(20);
const LEGACY_DAEMON_RESTART_MESSAGE: &str = "legacy daemon detected; restart daemon manually";

struct PidfileGuard {
    file: File,
}

impl PidfileGuard {
    fn acquire(path: &Path) -> Result<Self> {
        let mut file = open_lock_file(path)?;

        match file.try_lock() {
            Ok(()) => {}
            Err(TryLockError::WouldBlock) => {
                bail!("daemon already running (pidfile lock busy at {})", path.display())
            }
            Err(TryLockError::Error(err)) => {
                return Err(err)
                    .wrap_err_with(|| format!("could not lock daemon pidfile {}", path.display()));
            }
        }

        file.set_len(0)
            .wrap_err_with(|| format!("could not truncate daemon pidfile {}", path.display()))?;
        writeln!(file, "{}", std::process::id())
            .and_then(|()| writeln!(file, "{DAEMON_VERSION}"))
            .wrap_err_with(|| format!("could not write daemon pidfile {}", path.display()))?;

        Ok(Self { file })
    }
}

impl Drop for PidfileGuard {

View on GitHub (pinned to c0c717ab04)

Solutions

  1. Do nothing — the already-running daemon is the one to use; verify with `atuin daemon status` or `pgrep atuin`.
  2. If the old daemon is stale/unreachable, kill it (`pkill -f 'atuin daemon'`), confirm the pidfile lock is released, then restart.
  3. Point conflicting instances at separate ATUIN_HOME directories if you intentionally need multiple daemons.
  4. Prefer `atuin daemon start`/autostart over manual spawns so the lock logic is respected.

Example fix

// before
atuin daemon start   # error: daemon already running (pidfile lock busy at ...)
// after
atuin status  # or just use the CLI; existing daemon serves requests
Defensive patterns

Strategy: try-catch

Validate before calling

pgrep -f 'atuin daemon' >/dev/null || atuin daemon start

Try / catch

match atuin_daemon_start().await {
    Err(e) if e.to_string().contains("pidfile lock busy") => { /* daemon already running: proceed, it will serve us */ }
    Err(e) => return Err(e),
    Ok(()) => {}
}

Prevention

When it happens

Trigger: Starting `atuin daemon` (or a command that spawns it, e.g. with autostart) while another atuin daemon instance is already running and holding the lock on the pidfile (typically ~/.local/share/atuin/daemon.pid).

Common situations: Double-starting the daemon manually; systemd socket units racing an autostart; a previous daemon still alive in another session/container sharing the same home directory; testing daemons in CI against a shared HOME.

Related errors


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