Hmbown/CodeWhale · error
model catalog test lock
Error message
model catalog test lock
What it means
A test-support lock name, not a runtime error: TEST_CATALOG_LOCK is a LazyLock<Mutex<()>> that tests acquire (via test_catalog_lock) to serialize catalog tests that touch the shared state-dir cache file (OPENROUTER_CACHE_FILE under resolve_state_dir("catalog")). It exists so concurrent tests don't race on load_cached/save of the same on-disk cache; a failure would only be a poisoned-lock panic in tests.
Solutions
- Keep every test that reads or writes the shared catalog cache inside the test_catalog_lock guard
- If a lock-poisoning panic occurs, isolate which test panicked while holding the lock and fix that test
- Point catalog tests at an isolated temp state dir where possible so the global lock is unnecessary
Defensive patterns
Strategy: retry
When it happens
Trigger: Thrown at crates/models/src/model_catalog.rs:206 when the library encounters an invalid state.
Common situations: See trigger scenarios.
AI-assisted analysis of Hmbown/CodeWhale@433685b202 (2026-09-15).
Data as JSON: /api/errors/5232fc82b6da4152.
Report an issue: GitHub.
Appendix: source
Thrown at crates/models/src/model_catalog.rs:206
}
fn catalog_cache_read_path() -> Result<PathBuf> {
Ok(codewhale_config::resolve_state_dir("catalog")?.join(OPENROUTER_CACHE_FILE))
}
pub fn load_cached() -> Option<CatalogCache> {
let path = catalog_cache_read_path().ok()?;
let raw = std::fs::read_to_string(path).ok()?;
serde_json::from_str(&raw).ok()
}
#[cfg(any(test, feature = "test-support"))]
static TEST_CATALOG_LOCK: std::sync::LazyLock<std::sync::Mutex<()>> =
std::sync::LazyLock::new(|| std::sync::Mutex::new(()));
#[cfg(any(test, feature = "test-support"))]
pub fn test_catalog_lock() -> std::sync::MutexGuard<'static, ()> {
TEST_CATALOG_LOCK.lock().expect("model catalog test lock")
}
#[cfg(any(test, feature = "test-support"))]
pub struct ActiveCatalogGuard {
previous: MergedCatalog,
}
#[cfg(any(test, feature = "test-support"))]
impl Drop for ActiveCatalogGuard {
fn drop(&mut self) {
let mut active = active_catalog().write().expect("active catalog write lock");
*active = self.previous.clone();
}
}
#[cfg(any(test, feature = "test-support"))]
pub fn replace_active_catalog_for_test(catalog: MergedCatalog) -> ActiveCatalogGuard {
let mut active = active_catalog().write().expect("active catalog write lock");View on GitHub (pinned to 433685b202)