cjpais/Handy · critical · anyhow::Error
Transcription engine panicked: {}. The model has been unload
Error message
Transcription engine panicked: {}. The model has been unloaded and will reload on next attempt. What it means
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.
Source
Thrown at src-tauri/src/managers/transcription.rs:1444
{
let mut current_model = self
.current_model_id
.lock()
.unwrap_or_else(|e| e.into_inner());
*current_model = None;
}
let _ = self.app_handle.emit(
"model-state-changed",
ModelStateEvent {
event_type: "unloaded".to_string(),
model_id: None,
model_name: None,
error: Some(format!("Engine panicked: {}", panic_msg)),
},
);
return Err(anyhow::anyhow!(
"Transcription engine panicked: {}. The model has been unloaded and will reload on next attempt.",
panic_msg
));
}
};
let output_language = with_model_detected_language(
resolve_output_language_evidence(
&settings,
applied_language_hint.as_deref(),
&model_languages,
output_was_translated,
),
model_detected_language,
);
debug!("Output language evidence: {:?}", output_language);
(text, output_language, model_languages)View on GitHub (pinned to 98a4d80cce)
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
Example fix
// before — caller treats all errors alike
let text = tm.transcribe(audio)?;
// after — recognize the panic-recovery error, notify, and let the next attempt reload
let text = match tm.transcribe(audio) {
Ok(t) => t,
Err(e) if e.to_string().contains("engine panicked") => {
notify_user("Transcription engine crashed; retrying with a fresh model load");
tm.initiate_model_load(); // expedite the reload instead of waiting
wait_for_loading_completion(&app_handle);
tm.transcribe(audio)?
}
Err(e) => return Err(e),
}; Defensive patterns
Strategy: try-catch
Validate before calling
// Panics are native and cannot be pre-validated; only input hygiene helps anyhow::ensure!(audio.iter().all(|s| s.is_finite()), "refusing to feed NaN/inf audio to native engine"); anyhow::ensure!(!audio.is_empty(), "empty audio");
Try / catch
match tm.transcribe(audio.clone()) {
Err(e) if e.to_string().contains("engine panicked") => {
// Model was intentionally unloaded; a fresh load happens on next attempt.
// Notify, expedite the reload, retry once — then report if it repeats.
notify("Engine crashed — reloading model");
tm.initiate_model_load();
wait_for_loading_completion(&app_handle);
tm.transcribe(audio)
}
other => other,
} Prevention
- 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
When it happens
Trigger: 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.
Common situations: 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.
Related errors
- Model is not loaded for transcription.
- Model failed to load after auto-load attempt. Please check y
- threshold must be between 0.0 and 1.0
- Failed to create VAD: {e}
- Failed to create SileroVad: {}
AI-assisted analysis of cjpais/Handy@98a4d80cce (2026-08-16).
Data as JSON: /api/errors/2a1efe602e3f5eab.
Report an issue: GitHub.