{"record":{"id":"dfb4254b138111fa","repo":"atuinsh/atuin","slug":"timed-out-waiting-for-lock-at","errorCode":null,"errorMessage":"timed out waiting for lock at {}","messagePattern":"timed out waiting for lock at (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/atuin/src/command/client/daemon.rs","lineNumber":214,"sourceCode":"    let file = open_lock_file(path)?;\n\n    let outcome = Backoff::Linear(LOCK_POLL)\n        .retry_sync(\n            || match file.try_lock() {\n                Ok(()) => ControlFlow::Break(Ok(())),\n                Err(TryLockError::WouldBlock) => ControlFlow::Continue(()),\n                Err(TryLockError::Error(err)) => {\n                    ControlFlow::Break(Err(eyre!(\"could not lock {}: {err}\", path.display())))\n                }\n            },\n            timeout,\n        )\n        .await;\n\n    match outcome {\n        Ok(Ok(())) => Ok(file),\n        Ok(Err(err)) => Err(err),\n        Err(()) => bail!(\"timed out waiting for lock at {}\", path.display()),\n    }\n}\n\nasync fn wait_for_pidfile_available(path: &Path, timeout: Duration) -> Result<()> {\n    let file = wait_for_lock(path, timeout).await?;\n    file.unlock().wrap_err_with(|| format!(\"failed to unlock {}\", path.display()))?;\n    Ok(())\n}\n\nasync fn connect_client(settings: &Settings) -> Result<HistoryClient> {\n    HistoryClient::new(\n        #[cfg(not(unix))]\n        settings.daemon.tcp_port,\n        #[cfg(unix)]\n        settings.daemon.existing_socket_path().into_owned(),\n    )\n    .await\n}","sourceCodeStart":196,"sourceCodeEnd":232,"githubUrl":"https://github.com/atuinsh/atuin/blob/c0c717ab04c881764bcad4b3d169a507e2432643/crates/atuin/src/command/client/daemon.rs#L196-L232","documentation":"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>'.","triggerScenarios":"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).","commonSituations":"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.","solutions":["Retry after the blocking process exits — check `pgrep -f 'atuin daemon'` and wait for it to disappear.","Kill the process holding the lock (`pkill -f 'atuin daemon'`) if it should not be running, then retry.","Remove a stale pidfile only after confirming no daemon holds the lock, then restart.","Avoid NFS/shared homes for ATUIN_HOME so flock semantics are reliable."],"exampleFix":"// before\npkill -f 'atuin daemon' ; atuin daemon start  # may hit: timed out waiting for lock\n// after\npkill -f 'atuin daemon'; while pgrep -f 'atuin daemon' >/dev/null; do sleep 0.2; done\natuin daemon start","handlingStrategy":"retry","validationCode":"for i in $(seq 1 20); do pgrep -f 'atuin daemon' >/dev/null || break; sleep 0.5; done\natuin daemon start","typeGuard":null,"tryCatchPattern":"loop {\n    match start_daemon().await {\n        Err(e) if e.to_string().contains(\"timed out waiting for lock\") => continue_after_backoff(),\n        other => break other,\n    }\n}","preventionTips":["Wait for the old daemon to fully exit before restarting.","Keep ATUIN_HOME on a local filesystem, not NFS.","Use systemd unit ordering/ExecStartPre checks when managing via systemd."],"tags":["daemon","file-lock","timeout"],"backgroundTag":"request-timeout","analyzedSha":"c0c717ab04c881764bcad4b3d169a507e2432643","analyzedAt":"2026-09-12T07:40:01.341Z","contentChangedAt":"2026-09-12T07:40:01.341Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}