aaif-goose/goose · error
Failed to acquire registry lock
Error message
Failed to acquire registry lock
What it means
list_models() takes the global registry mutex via get_registry() (a std::sync::Mutex in local_model_registry.rs). The error means lock() returned Err, i.e. the mutex is poisoned: some other thread panicked while holding this lock, and std marks the mutex poisoned forever after. Every subsequent list_models() call in this process then fails with this message. The registry data itself is fine (persisted on disk); only the in-process lock is unusable.
Source
Thrown at crates/goose-local-inference/src/management.rs:53
#[derive(Clone)]
struct LocalModelSelection {
repo_id: String,
backend_id: String,
variant_id: Option<String>,
}
pub async fn list_models() -> Result<LocalInferenceModelsListResponse> {
ensure_featured_models_current().await?;
let runtime = management_runtime()?;
let recommended_id = recommend_local_model(&runtime);
let loaded_model_ids = crate::loaded_model_ids()
.await
.map_err(|error| anyhow!(error.to_string()))?;
let registry = get_registry()
.lock()
.map_err(|_| anyhow!("Failed to acquire registry lock"))?;
let mut models: Vec<LocalInferenceModelDto> = registry
.list_models()
.iter()
.map(|entry| local_model_to_dto(entry, &recommended_id, &loaded_model_ids))
.collect();
models.sort_by(|a, b| {
let a_downloaded = a.status.state == LocalInferenceDownloadState::Downloaded;
let b_downloaded = b.status.state == LocalInferenceDownloadState::Downloaded;
match (b_downloaded, a_downloaded) {
(true, false) => std::cmp::Ordering::Greater,
(false, true) => std::cmp::Ordering::Less,
_ => a.id.cmp(&b.id),
}
});
Ok(LocalInferenceModelsListResponse { models })
}View on GitHub (pinned to 3810898a74)
Solutions
- Restart the goose process — the mutex is in-memory only; the on-disk registry is reloaded cleanly.
- Find the FIRST panic in the logs (search for 'panicked at'); this lock error is only a symptom. Fix the panic inside that critical section.
- Audit all code holding the registry guard for unwrap/expect/indexing (delete_model, add_model, sync_with_featured, list_models_mut loops) and make them return Result instead of panicking.
- Keep critical sections minimal: clone the needed data out of the registry and drop the guard before doing I/O or downloads.
Example fix
// before
let registry = get_registry()
.lock()
.map_err(|_| anyhow!("Failed to acquire registry lock"))?;
// after: recover from poisoning — registry data is still a valid struct
let registry = get_registry()
.lock()
.unwrap_or_else(|poisoned| poisoned.into_inner()); Defensive patterns
Strategy: try-catch
Try / catch
match management::list_models().await {
Ok(models) => { /* render models */ }
Err(e) if e.to_string().contains("registry lock") => {
// poisoned mutex: earlier panic corrupted in-process state; restart required
show_error("Model registry state corrupted by an earlier panic. Restart goose.");
}
Err(e) => show_error(e),
} Prevention
- Never call unwrap/expect/unchecked indexing while holding the registry guard.
- Keep critical sections minimal: clone needed data, drop the guard, then do I/O.
- Restart the process after any panic in download/registry threads instead of continuing.
- Consider parking_lot::Mutex (non-poisoning) or .unwrap_or_else(|e| e.into_inner()) recovery for this registry.
When it happens
Trigger: Calling the local-inference model list API after any thread panicked inside a registry critical section — e.g. a panic in delete_model, add_model during download registration, or sync_with_featured/enrich inside ensure_featured_models_current() (which runs first in list_models). Any unwrap/expect/index-out-of-bounds executed while the guard is held poisons the mutex.
Common situations: A background download thread panicking mid-write to registry.json (disk full, permission denied, serialization bug); a crash during featured-model backfill at startup; concurrent CLI and desktop sessions in one process; upgrading goose versions where a registry field parse panicked in an old code path.
Related errors
- Failed to acquire registry lock
- Resolved GGUF model has no local files
- Model not found: {}
- Model not found
- Failed to acquire registry lock
AI-assisted analysis of aaif-goose/goose@3810898a74 (2026-08-16).
Data as JSON: /api/errors/81b0d200d42d5504.
Report an issue: GitHub.