cjpais/Handy · error · anyhow::Error

Transcription engine panicked

Error message

Transcription engine panicked: {}. The model has been unloaded and will reload on next attempt.

What it means

The engine call is wrapped in catch_unwind(AssertUnwindSafe(...)); this error fires when the native engine (GGML or ONNX Runtime code) panicked instead of returning Err. On panic the engine is deliberately not put back (mutex stays unpoisoned), a model-state-changed "unloaded" event is emitted with "Engine panicked: {msg}", and the error promises the model reloads on the next attempt.

Solutions

  1. Simply retry once — the engine was unloaded and the next transcribe attempt reloads the model
  2. Read the panic message in the log to identify the native assertion; search the Handy tracker for that message
  3. Update the app (bundles newer transcribe-cpp/transcribe-rs with panic fixes)
  4. Switch to a different engine/model to keep working while the panic is investigated
  5. Report with logs if reproducible — include audio conditions (length, device, sample rate)
Defensive patterns

Strategy: fallback

Validate before calling

// sanitize audio before it reaches native code
let clean: Vec<f32> = audio.iter().map(|s| if s.is_finite() { *s } else { 0.0 }).collect();

Try / catch

match manager.transcribe(audio.clone()) {
    Err(e) if e.to_string().starts_with("Transcription engine panicked") => {
        // engine was unloaded; a fresh attempt reloads the model — retry exactly once
        manager.transcribe(audio)
    }
    other => other,
}

Prevention

When it happens

Trigger: Any panic inside the matched engine arm: native assertions in transcribe-cpp/transcribe-rs (index out of bounds, unwrap on None), audio containing NaN/inf samples driving a panic in DSP code, or FFI-level crashes surfaced as Rust panics.

Common situations: Buggy engine version for a newly added arch; pathological audio (silence, clipping, corrupt float samples); edge-case sample rates; bugs fixed in later library updates.

Related errors


AI-assisted analysis of cjpais/Handy@ba10ce1943 (2026-08-17). Data as JSON: /api/errors/2a1efe602e3f5eab. Report an issue: GitHub.

Appendix: source

Thrown at src-tauri/src/managers/transcription.rs:1470

                    {
                        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 ba10ce1943)