{"record":{"id":"5232fc82b6da4152","repo":"Hmbown/CodeWhale","slug":"model-catalog-test-lock","errorCode":null,"errorMessage":"model catalog test lock","messagePattern":"model catalog test lock","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/models/src/model_catalog.rs","lineNumber":206,"sourceCode":"}\n\nfn catalog_cache_read_path() -> Result<PathBuf> {\n    Ok(codewhale_config::resolve_state_dir(\"catalog\")?.join(OPENROUTER_CACHE_FILE))\n}\n\npub fn load_cached() -> Option<CatalogCache> {\n    let path = catalog_cache_read_path().ok()?;\n    let raw = std::fs::read_to_string(path).ok()?;\n    serde_json::from_str(&raw).ok()\n}\n\n#[cfg(any(test, feature = \"test-support\"))]\nstatic TEST_CATALOG_LOCK: std::sync::LazyLock<std::sync::Mutex<()>> =\n    std::sync::LazyLock::new(|| std::sync::Mutex::new(()));\n\n#[cfg(any(test, feature = \"test-support\"))]\npub fn test_catalog_lock() -> std::sync::MutexGuard<'static, ()> {\n    TEST_CATALOG_LOCK.lock().expect(\"model catalog test lock\")\n}\n\n#[cfg(any(test, feature = \"test-support\"))]\npub struct ActiveCatalogGuard {\n    previous: MergedCatalog,\n}\n\n#[cfg(any(test, feature = \"test-support\"))]\nimpl Drop for ActiveCatalogGuard {\n    fn drop(&mut self) {\n        let mut active = active_catalog().write().expect(\"active catalog write lock\");\n        *active = self.previous.clone();\n    }\n}\n\n#[cfg(any(test, feature = \"test-support\"))]\npub fn replace_active_catalog_for_test(catalog: MergedCatalog) -> ActiveCatalogGuard {\n    let mut active = active_catalog().write().expect(\"active catalog write lock\");","sourceCodeStart":188,"sourceCodeEnd":224,"githubUrl":"https://github.com/Hmbown/CodeWhale/blob/433685b2024e7bc4c99e1e2e326bcad39b4d9d65/crates/models/src/model_catalog.rs#L188-L224","documentation":"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.","triggerScenarios":"Thrown at crates/models/src/model_catalog.rs:206 when the library encounters an invalid state.","commonSituations":"See trigger scenarios.","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"],"exampleFix":null,"handlingStrategy":"retry","validationCode":null,"typeGuard":null,"tryCatchPattern":null,"preventionTips":[],"tags":[],"backgroundTag":null,"analyzedSha":"433685b2024e7bc4c99e1e2e326bcad39b4d9d65","analyzedAt":"2026-09-15T12:24:24.634Z","contentChangedAt":"2026-09-15T12:24:24.634Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}