atuinsh/atuin · error

timed out waiting for lock at {}

Error message

timed out waiting for lock at {}

What it means

wait_for_lock polls for a file lock on the daemon pidfile with a bounded timeout (via tokio::time::timeout around a blocking lock attempt). If the lock is not obtained before the deadline, the timeout elapses (Err(())) and the caller bails with 'timed out waiting for lock at <path>'.

Source

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

    let file = open_lock_file(path)?;

    let outcome = Backoff::Linear(LOCK_POLL)
        .retry_sync(
            || match file.try_lock() {
                Ok(()) => ControlFlow::Break(Ok(())),
                Err(TryLockError::WouldBlock) => ControlFlow::Continue(()),
                Err(TryLockError::Error(err)) => {
                    ControlFlow::Break(Err(eyre!("could not lock {}: {err}", path.display())))
                }
            },
            timeout,
        )
        .await;

    match outcome {
        Ok(Ok(())) => Ok(file),
        Ok(Err(err)) => Err(err),
        Err(()) => bail!("timed out waiting for lock at {}", path.display()),
    }
}

async fn wait_for_pidfile_available(path: &Path, timeout: Duration) -> Result<()> {
    let file = wait_for_lock(path, timeout).await?;
    file.unlock().wrap_err_with(|| format!("failed to unlock {}", path.display()))?;
    Ok(())
}

async fn connect_client(settings: &Settings) -> Result<HistoryClient> {
    HistoryClient::new(
        #[cfg(not(unix))]
        settings.daemon.tcp_port,
        #[cfg(unix)]
        settings.daemon.existing_socket_path().into_owned(),
    )
    .await
}

View on GitHub (pinned to c0c717ab04)

Solutions

  1. Retry after the blocking process exits — check `pgrep -f 'atuin daemon'` and wait for it to disappear.
  2. Kill the process holding the lock (`pkill -f 'atuin daemon'`) if it should not be running, then retry.
  3. Remove a stale pidfile only after confirming no daemon holds the lock, then restart.
  4. Avoid NFS/shared homes for ATUIN_HOME so flock semantics are reliable.

Example fix

// before
pkill -f 'atuin daemon' ; atuin daemon start  # may hit: timed out waiting for lock
// after
pkill -f 'atuin daemon'; while pgrep -f 'atuin daemon' >/dev/null; do sleep 0.2; done
atuin daemon start
Defensive patterns

Strategy: retry

Validate before calling

for i in $(seq 1 20); do pgrep -f 'atuin daemon' >/dev/null || break; sleep 0.5; done
atuin daemon start

Try / catch

loop {
    match start_daemon().await {
        Err(e) if e.to_string().contains("timed out waiting for lock") => continue_after_backoff(),
        other => break other,
    }
}

Prevention

When it happens

Trigger: wait_for_pidfile_available or ensure_daemon_running waiting for another process to release the pidfile lock while it keeps holding it past the timeout duration (e.g. a long-lived daemon or a hung process).

Common situations: Restarting the daemon while the old one is slow to shut down; a wedged daemon holding the lock indefinitely; heavily loaded machine delaying daemon exit; NFS/network filesystems where flock behaves poorly.

Understand the failure class

Background: Request timed out: what client-side request timeouts mean across libraries (Request timed out, TIMED_OUT, APITimeoutError) — this error's family across 39 libraries.

Related errors


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