Hmbown/CodeWhale · error
catalog cache unavailable
Error message
catalog cache unavailable
What it means
`ensure_cache_loaded` lazily loads the on-disk provider-catalog cache into a process-global `CACHE` behind a `RwLock`. If acquiring the write lock fails, the RwLock is poisoned — meaning a thread panicked while holding it — and the library surfaces the opaque 'catalog cache unavailable' error rather than risk reading inconsistent cache state.
Solutions
- Restart the process — RwLock poisoning is process-global and clears only on restart.
- Reproduce the original panic (it appears earlier in logs) and fix that root-cause bug; this error is only the echo.
- Check the catalog cache file for malformed content that could make the loading code panic.
Defensive patterns
Strategy: try-catch
Try / catch
match ensure_cache_loaded() {
Err(e) if e.to_string().contains("catalog cache unavailable") => {
// poisoned lock: skip cache, go straight to network refresh, log for restart
log::warn("catalog cache poisoned; bypassing until restart");
refresh_from_network()
}
r => r,
} Prevention
- Investigate any earlier panic in cache-loading code — it is the real bug
- Monitor logs for the first panic; this error always echoes it
- Restart long-lived processes after any catalog-cache panic
When it happens
Trigger: `cached_entry_for_route` (via `ensure_cache_loaded`) is called after some earlier thread panicked while mutating `CACHE` (e.g. a bug in cache deserialization/persist code), leaving the RwLock poisoned for the rest of the process.
Common situations: A latent panic in cache-loading code runs once early in the session; every later catalog lookup then fails with this error even though the disk file is fine; usually seen only after another suppressed panic.
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
- Shell tracking hit an internal error — restart Codewhale to…
- xAI OAuth lifecycle lock was poisoned
- A saved trace identifier collided. Try saving again.
- a window change is already in progress
AI-assisted analysis of Hmbown/CodeWhale@433685b202 (2026-09-15).
Data as JSON: /api/errors/d35bab318b759405.
Report an issue: GitHub.
Appendix: source
Thrown at crates/tui/src/provider_catalog_live.rs:684
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 433685b202)