{"record":{"id":"2a1efe602e3f5eab","repo":"cjpais/Handy","slug":"transcription-engine-panicked-the-model-has-b","errorCode":null,"errorMessage":"Transcription engine panicked: {}. The model has been unloaded and will reload on next attempt.","messagePattern":"Transcription engine panicked: (.+?)\\. The model has been unloaded and will reload on next attempt\\.","errorType":"exception","errorClass":"anyhow::Error","httpStatus":null,"severity":"critical","filePath":"src-tauri/src/managers/transcription.rs","lineNumber":1444,"sourceCode":"                    {\n                        let mut current_model = self\n                            .current_model_id\n                            .lock()\n                            .unwrap_or_else(|e| e.into_inner());\n                        *current_model = None;\n                    }\n\n                    let _ = self.app_handle.emit(\n                        \"model-state-changed\",\n                        ModelStateEvent {\n                            event_type: \"unloaded\".to_string(),\n                            model_id: None,\n                            model_name: None,\n                            error: Some(format!(\"Engine panicked: {}\", panic_msg)),\n                        },\n                    );\n\n                    return Err(anyhow::anyhow!(\n                        \"Transcription engine panicked: {}. The model has been unloaded and will reload on next attempt.\",\n                        panic_msg\n                    ));\n                }\n            };\n\n            let output_language = with_model_detected_language(\n                resolve_output_language_evidence(\n                    &settings,\n                    applied_language_hint.as_deref(),\n                    &model_languages,\n                    output_was_translated,\n                ),\n                model_detected_language,\n            );\n            debug!(\"Output language evidence: {:?}\", output_language);\n\n            (text, output_language, model_languages)","sourceCodeStart":1426,"sourceCodeEnd":1462,"githubUrl":"https://github.com/cjpais/Handy/blob/98a4d80cce8ad41efec2a419b59d9e81229a35d7/src-tauri/src/managers/transcription.rs#L1426-L1462","documentation":"A panic inside the transcription engine call was caught by catch_unwind (used deliberately so a native crash cannot poison the manager's mutexes and hang the app). Handy recovers by NOT returning the engine (it is dropped, freeing native resources), clearing current_model_id, emitting a model-state-changed/unloaded event, and returning this error; the next transcription attempt re-loads the model automatically. The panic message identifies the native fault — typically a bug in the GGML/ONNX engine layer or bad state such as NaN audio reaching native code.","triggerScenarios":"The closure around the per-engine transcribe call panics: index/bounds panic in native binding code, assertion failures in transcribe-cpp or transcribe-rs, malformed audio (NaN/inf samples) triggering library assumptions, or an engine/arch-specific bug on a particular backend.","commonSituations":"Specific model+accelerator combinations hitting a native bug (fixed in later releases); corrupted model files driving native code into bad state; exotic audio input (all-NaN buffers after resampling); GPU driver crashes surfacing as panics in bindings.","solutions":["Simply retry — the model reloads on the next attempt and the panic is often non-deterministic","Note the panic message; if it names a specific engine/backend, switch model or accelerator (e.g. CPU) to avoid the faulty path","Re-download the implicated model in case of corruption","Update Handy — engine panics are treated as bugs and patched","Report the panic message plus model/OS/backend to the issue tracker if reproducible"],"exampleFix":"// before — caller treats all errors alike\nlet text = tm.transcribe(audio)?;\n\n// after — recognize the panic-recovery error, notify, and let the next attempt reload\nlet text = match tm.transcribe(audio) {\n    Ok(t) => t,\n    Err(e) if e.to_string().contains(\"engine panicked\") => {\n        notify_user(\"Transcription engine crashed; retrying with a fresh model load\");\n        tm.initiate_model_load(); // expedite the reload instead of waiting\n        wait_for_loading_completion(&app_handle);\n        tm.transcribe(audio)?\n    }\n    Err(e) => return Err(e),\n};","handlingStrategy":"try-catch","validationCode":"// Panics are native and cannot be pre-validated; only input hygiene helps\nanyhow::ensure!(audio.iter().all(|s| s.is_finite()), \"refusing to feed NaN/inf audio to native engine\");\nanyhow::ensure!(!audio.is_empty(), \"empty audio\");","typeGuard":null,"tryCatchPattern":"match tm.transcribe(audio.clone()) {\n    Err(e) if e.to_string().contains(\"engine panicked\") => {\n        // Model was intentionally unloaded; a fresh load happens on next attempt.\n        // Notify, expedite the reload, retry once — then report if it repeats.\n        notify(\"Engine crashed — reloading model\");\n        tm.initiate_model_load();\n        wait_for_loading_completion(&app_handle);\n        tm.transcribe(audio)\n    }\n    other => other,\n}","preventionTips":["Always sanitize audio (finite samples, sane lengths) before feeding native engines","Keep models and GPU drivers updated; many panics are engine bugs fixed upstream","Note the model+accelerator pair when a panic occurs and switch combinations if reproducible","Report reproducible panics with the panic message — catch_unwind keeps the app alive so logs survive"],"tags":["rust","panic","catch-unwind","native","model-lifecycle","recovery"],"backgroundTag":"native-library-panic","analyzedSha":"98a4d80cce8ad41efec2a419b59d9e81229a35d7","analyzedAt":"2026-08-16T20:58:09.966Z","schemaVersion":2},"datasetVersion":"2026-08-16T23:17:17.608Z"}