Hmbown/CodeWhale · error · anyhow::Error

xAI OAuth lifecycle lock was poisoned

Error message

xAI OAuth lifecycle lock was poisoned

What it means

with_xai_oauth_lifecycle_lock first takes a process-wide std::sync::Mutex to serialize xAI OAuth mutations inside the process, then a cross-process fd-lock on the lock file. This error means the in-process Mutex was poisoned: a previous holder panicked while holding it, so lock() returned Err. The poison is per-process; a fresh process acquires cleanly, and on-disk state stays consistent because the file lock serializes across processes.

Source

Thrown at crates/config/src/xai_credentials.rs:130

}

pub fn legacy_xai_oauth_path() -> Result<PathBuf> {
    Ok(xai_oauth_credentials_dir()?.join(LEGACY_XAI_OAUTH_FILE_NAME))
}

/// Serialize every Codewhale-owned xAI OAuth lifecycle mutation across threads
/// and processes while pinning the lexical credentials directory.
///
/// Lock order is always xAI lifecycle first, then config document. Callers must
/// not invoke this function recursively.
pub fn with_xai_oauth_lifecycle_lock<T>(
    operation: impl FnOnce(&XaiOAuthCredentialStore) -> Result<T>,
) -> Result<T> {
    static PROCESS_LOCK: OnceLock<Mutex<()>> = OnceLock::new();
    let _process_guard = PROCESS_LOCK
        .get_or_init(|| Mutex::new(()))
        .lock()
        .map_err(|_| anyhow::anyhow!("xAI OAuth lifecycle lock was poisoned"))?;
    let store = XaiOAuthCredentialStore::open()?;
    let lock_file = store.open_lock_file()?;
    let mut lock = fd_lock::RwLock::new(lock_file);
    let _guard = lock.write().with_context(|| {
        format!(
            "failed to acquire xAI OAuth lifecycle lock in {}",
            crate::quote_os_path(store.directory())
        )
    })?;
    operation(&store)
}

/// Run an authority mode switch while the prior owned OAuth epoch is hidden
/// from concurrent Codewhale readers. A failed authority mutation restores the
/// old files; a successful mutation permanently removes them.
pub fn with_xai_oauth_revocation_transaction<T>(
    operation: impl FnOnce() -> Result<T>,
) -> Result<T> {

View on GitHub (pinned to 8880682c63)

Solutions

  1. Restart the process; the poison clears on restart
  2. Look earlier in the logs for the panic that poisoned the lock; that panic is the real bug, report it
  3. Do not delete credential files to fix this; the fd-locked on-disk state was protected across the panic
Defensive patterns

Strategy: try-catch

Try / catch

Map this Err to a 'restart required' signal: the poison is process-local, so spawn a fresh process or disable xAI operations for this session instead of retrying in-process. Pair it with a log scan for the original panic.

Prevention

When it happens

Trigger: Any panic inside the xAI OAuth lifecycle operation (or between acquisition and release) in this process; every subsequent with_xai_oauth_lifecycle_lock call in the same process then fails with this message.

Common situations: A long-lived process (daemon, test harness) where an earlier xAI login/logout/token-refresh panicked; later xAI operations all report the poisoned lock although the credential files on disk are fine.

Related errors


AI-assisted analysis of Hmbown/CodeWhale@8880682c63 (2026-08-16). Data as JSON: /api/errors/d2e0fad1dc51f5e8. Report an issue: GitHub.