{"record":{"id":"a154ae02a272d6b8","repo":"aaif-goose/goose","slug":"failed-to-lock-transcriber","errorCode":null,"errorMessage":"Failed to lock transcriber: {}","messagePattern":"Failed to lock transcriber: (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/goose/src/dictation/providers.rs","lineNumber":150,"sourceCode":"}\n\n#[cfg(feature = \"local-inference\")]\npub async fn transcribe_local(audio_bytes: Vec<u8>) -> Result<String> {\n    tokio::task::spawn_blocking(move || {\n        let config = Config::global();\n        let model_id = config\n            .get(LOCAL_WHISPER_MODEL_CONFIG_KEY, false)\n            .ok()\n            .and_then(|v| v.as_str().map(|s| s.to_string()))\n            .ok_or_else(|| anyhow::anyhow!(\"Local Whisper model not configured\"))?;\n\n        let model = super::whisper::get_model(&model_id)\n            .ok_or_else(|| anyhow::anyhow!(\"Unknown model: {}\", model_id))?;\n        let model_path = model.local_path();\n\n        let mut transcriber_lock = LOCAL_TRANSCRIBER\n            .lock()\n            .map_err(|e| anyhow::anyhow!(\"Failed to lock transcriber: {}\", e))?;\n\n        let model_path_str = model_path.to_string_lossy().to_string();\n        let needs_reload = match transcriber_lock.as_ref() {\n            None => true,\n            Some((cached_path, _)) => cached_path != &model_path_str,\n        };\n\n        if needs_reload {\n            tracing::info!(\"Loading Whisper model from: {}\", model_path.display());\n\n            let transcriber = super::whisper::WhisperTranscriber::new_with_tokenizer(\n                &model_id,\n                &model_path,\n                WHISPER_TOKENIZER_JSON,\n            )?;\n\n            *transcriber_lock = Some((model_path_str, transcriber));\n        }","sourceCodeStart":132,"sourceCodeEnd":168,"githubUrl":"https://github.com/aaif-goose/goose/blob/3810898a7447ec3299be72e223d3570a7aabf0ab/crates/goose/src/dictation/providers.rs#L132-L168","documentation":"The local Whisper transcriber is cached in a global std::sync::Mutex (LOCAL_TRANSCRIBER) shared across calls so the model loads once. `.lock()` returns Err only when the mutex is poisoned — another thread panicked while holding it (model load, file read, or inference panic). The {error} is the std::sync::PoisonError.","triggerScenarios":"A previous transcribe_local call panicked inside spawn_blocking while holding the lock (corrupted model file, decode error, IO panic); every subsequent transcription in the same process then fails immediately with this error.","commonSituations":"Interrupted model download leaving a truncated file; disk errors while mmapping the model; a panic during transcription after which the process keeps serving requests.","solutions":["Look upward in the logs for the ORIGINAL panic that poisoned the mutex — fixing that is the real fix","Restart the process: poisoning is per-process state and a fresh start clears it","If the model file is corrupt (partial download), remove it so the next start re-downloads cleanly"],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"match transcribe_local(audio).await {\n    Err(e) if e.to_string().contains(\"Failed to lock transcriber\") => {\n        // mutex poisoned by an earlier panic: surface the original panic from logs,\n        // stop accepting dictation, and restart the process to recover\n    }\n    other => other,\n}","preventionTips":["Catch panics inside the transcription path so a model-load failure cannot poison LOCAL_TRANSCRIBER (e.g. std::panic::catch_unwind around model load/inference)","Verify model files are fully downloaded before enabling local dictation (is_downloaded)","Re-download the model when files are truncated or corrupted after an interrupted download"],"tags":["dictation","whisper","concurrency","mutex","panic"],"backgroundTag":null,"analyzedSha":"3810898a7447ec3299be72e223d3570a7aabf0ab","analyzedAt":"2026-08-16T10:14:26.282Z","schemaVersion":2},"datasetVersion":"2026-08-16T13:17:31.715Z"}