{"record":{"id":"50842c791d244b32","repo":"zeroclaw-labs/zeroclaw","slug":"timed-out-waiting-for-auth-profile-lock-at","errorCode":null,"errorMessage":"Timed out waiting for auth profile lock at {}","messagePattern":"Timed out waiting for auth profile lock at (.+?)","errorType":"exception","errorClass":"anyhow::Error","httpStatus":null,"severity":"error","filePath":"crates/zeroclaw-providers/src/auth/profiles.rs","lineNumber":544,"sourceCode":"                                    .with_attrs(::serde_json::json!({\"e\": format!(\"{:?}\", e)})),\n                                    \"Failed to remove auth profile lock file: \"\n                                );\n                            })\n                            .ok();\n                        return Err(e).with_context(|| {\n                            format!(\n                                \"Failed to write auth profile lock at {}\",\n                                self.lock_path.display()\n                            )\n                        });\n                    }\n                    return Ok(AuthProfileLockGuard {\n                        lock_path: self.lock_path.clone(),\n                    });\n                }\n                Err(e) if e.kind() == std::io::ErrorKind::AlreadyExists => {\n                    if waited >= LOCK_TIMEOUT_MS {\n                        anyhow::bail!(\n                            \"Timed out waiting for auth profile lock at {}\",\n                            self.lock_path.display()\n                        );\n                    }\n                    sleep(Duration::from_millis(LOCK_WAIT_MS)).await;\n                    waited = waited.saturating_add(LOCK_WAIT_MS);\n                }\n                Err(e) => {\n                    return Err(e).with_context(|| {\n                        format!(\n                            \"Failed to create auth profile lock at {}\",\n                            self.lock_path.display()\n                        )\n                    });\n                }\n            }\n        }\n    }","sourceCodeStart":526,"sourceCodeEnd":562,"githubUrl":"https://github.com/zeroclaw-labs/zeroclaw/blob/88bb9c8533fc57ed7a03e36ca7c9ed2bf8336dcc/crates/zeroclaw-providers/src/auth/profiles.rs#L526-L562","documentation":"`acquire_lock` tries to create `auth-profiles.lock` with create-new semantics, polls every 50 ms, and gives up after 10 s (LOCK_TIMEOUT_MS). Every store operation — load, list, upsert, remove, set_active, clear_active — takes this lock, so one long-lived holder blocks all auth profile I/O across processes. The guard deletes the file on Drop, but a SIGKILLed process leaks it until removed.","triggerScenarios":"A zeroclaw daemon is mid token-refresh (holding the lock) while the CLI also touches the store; a previous run was killed with SIGKILL leaving a stale lock file; tight loops doing per-request `load()` calls extend hold time past the 10 s budget.","commonSituations":"CLI plus background service on the same machine; test suites that drop guards late; crash leftovers from forced shutdowns.","solutions":["Retry once after a short wait — a legitimate holder usually finishes quickly","Inspect the lock file: its first line is `pid=<n>`; if that pid is not alive, delete the stale lock file","Cache loaded profile data in-process instead of calling `load()` per request","Keep lock-guard lifetimes short: drop the store result guard before unrelated awaits"],"exampleFix":"// before: per-request store hit under a busy daemon\nlet data = store.load().await?; // \"Timed out waiting for auth profile lock at ...\"\n\n// after: on timeout, clear a dead-holder lock and retry once\nlet data = match store.load().await {\n    Ok(d) => d,\n    Err(e) if e.to_string().contains(\"Timed out waiting for auth profile lock\") => {\n        if lock_holder_dead(&lock_path).await { tokio::fs::remove_file(&lock_path).await?; }\n        store.load().await?\n    }\n    Err(e) => return Err(e),\n};","handlingStrategy":"retry","validationCode":"async fn lock_holder_is_dead(path: &std::path::Path) -> bool {\n    let body = tokio::fs::read_to_string(path).await.unwrap_or_default();\n    let pid = body.trim().strip_prefix(\"pid=\").and_then(|p| p.parse::<u32>().ok());\n    !std::path::Path::new(&format!(\"/proc/{}\", pid.unwrap_or(0))).exists()\n}","typeGuard":null,"tryCatchPattern":"match store.load().await {\n    Ok(d) => d,\n    Err(e) if e.to_string().contains(\"Timed out waiting for auth profile lock\") => {\n        if lock_holder_is_dead(&lock_path).await {\n            tokio::fs::remove_file(&lock_path).await?; // stale lock from a killed process\n        }\n        store.load().await? // one retry\n    }\n    Err(e) => return Err(e),\n}","preventionTips":["Cache loaded profile data instead of calling load() per request","Drop lock guards before awaits unrelated to the store","After SIGKILL crashes, check the lock file's pid= line against /proc before deleting"],"tags":["file-lock","concurrency","timeout","auth","rust"],"backgroundTag":"file-lock-timeout","analyzedSha":"88bb9c8533fc57ed7a03e36ca7c9ed2bf8336dcc","analyzedAt":"2026-08-23T01:07:41.857Z","schemaVersion":2},"datasetVersion":"2026-08-23T08:06:27.607Z"}