Hmbown/CodeWhale · error
catalog cache unavailable
Error message
catalog cache unavailable
What it means
ensure_cache_loaded takes a write lock on the in-memory catalog CACHE (a RwLock) while first loading from disk. If the lock is poisoned — a previous thread panicked while holding it — lock().write() returns Err and this error is thrown instead of proceeding.
Solutions
- Fix the underlying panic that poisoned the CACHE lock (check earlier panics in logs)
- Restart the TUI process to get a fresh, unpoisoned lock
- Wrap cache mutations in catch_unwind or restructure so no code panics while holding the lock
- If persistent, delete the on-disk catalog cache to rule out malformed-data-driven panics
Example fix
// before
let mut cache = CACHE.write().map_err(|_| anyhow::anyhow!("catalog cache unavailable"))?;
// after
let mut cache = CACHE.write().unwrap_or_else(|e| e.into_inner()); // recover from poison Defensive patterns
Strategy: retry
Try / catch
match ensure_cache_loaded() {
Err(e) if e.to_string().contains("catalog cache unavailable") => {
// lock poisoned: restart session or fall back to network refresh
return fallback_to_live_catalog();
}
Ok(()) => {},
Err(e) => return Err(e),
} Prevention
- Avoid panic! while holding catalog cache locks; return Result instead
- Recover poison with unwrap_or_else(|e| e.into_inner()) in this module
- Watch logs for earlier catalog panics — poison is always secondary
- Restart the TUI after any catalog-related panic
When it happens
Trigger: Calling cached_entry_for_route (which calls ensure_cache_loaded) after any panic occurred in another thread while mutating the CACHE map, e.g. during disk load or scope writes.
Common situations: After a panic elsewhere in catalog code; long-running TUI sessions that hit an earlier bug and now permanently fail catalog cache reads.
Understand the failure class
Background: "This is a bug, please report it": internal invariant violations, unreachable panics, and SNH errors explained — this error's family across 47 libraries.
Related errors
- Frame lock failed
- bounded fragment module missing required marker
- child wall-time budget exhausted
- {}
- Failed to steer turn
AI-assisted analysis of Hmbown/CodeWhale@73e0f67d83 (2026-09-22).
Data as JSON: /api/errors/d35bab318b759405.
Report an issue: GitHub.
Appendix: source
Thrown at crates/tui/src/provider_catalog_live.rs:677
fn load_from_disk() -> Option<ProviderCatalogCache> {
let path = cache_path()?;
if !path.is_file() {
return None;
}
let lock_file = open_cache_lock(&cache_lock_path(&path)).ok()?;
let lock = fd_lock::RwLock::new(lock_file);
let _guard = lock.read().ok()?;
load_from_disk_unlocked(&path)
}
fn ensure_cache_loaded() -> Result<()> {
if DISK_LOADED.load(Ordering::Acquire) {
return Ok(());
}
let mut cache = CACHE
.write()
.map_err(|_| anyhow::anyhow!("catalog cache unavailable"))?;
if DISK_LOADED.load(Ordering::Acquire) {
return Ok(());
}
if let Some(path) = cache_path() {
match fs::symlink_metadata(&path) {
Err(err) if err.kind() == std::io::ErrorKind::NotFound => {}
Err(err) => return Err(err.into()),
Ok(_) => {
let loaded = load_from_disk().context("invalid provider catalog cache")?;
for (key, entry) in loaded.entries {
if cache
.entries
.get(&key)
.is_none_or(|local| entry.fetched_at >= local.fetched_at)
{
cache.entries.insert(key, entry);
}
}View on GitHub (pinned to 73e0f67d83)