cjpais/Handy · error · anyhow::Error

Failed to load whisper model

Error message

Failed to load whisper model {}: {}

What it means

Raised when transcribe-cpp's Model::load_with(path, ModelOptions{backend, gpu_device}) fails to load a Whisper-family GGUF. The failure comes from the native GGML layer: unreadable/corrupt GGUF file, unsupported model architecture or quantization, or backend initialization failure (e.g. Vulkan driver problems) including under Auto when even the CPU fallback path fails. A 'loading_failed' event is emitted with the same message before returning.

Solutions

  1. Check the tail of the error for the native cause; 'failed to open/read file' means the GGUF path is bad or truncated — re-download the model.
  2. Switch the transcription accelerator setting to CPU and retry; if CPU loads fine, the problem is GPU backend init (update Vulkan/GPU drivers or use CPU).
  3. Re-download the model from the catalog to rule out corruption (hash-verified on download).
  4. Try a different quantization/size variant of the same model — the format may be unsupported by the bundled transcribe-cpp build.
  5. On Linux, verify Vulkan is installed (vulkaninfo) if requesting Vulkan/Auto.

Example fix

// before
let model = Model::load_with(&model_path, &ModelOptions { backend, gpu_device })?;

// after — fall back to CPU when the requested backend cannot load the model
let model = match Model::load_with(&model_path, &opts) {
    Ok(m) => m,
    Err(e) if !matches!(backend, Backend::Cpu) => {
        warn!("load with {backend:?} failed ({e}); retrying on CPU");
        Model::load_with(&model_path, &ModelOptions { backend: Backend::Cpu, gpu_device: None })?
    }
    Err(e) => return Err(anyhow::anyhow!("Failed to load whisper model {model_id}: {e}")),
};
Defensive patterns

Strategy: fallback

Validate before calling

// Cheap pre-flight: file exists and is non-trivial before the native load
let meta = std::fs::metadata(&model_path)?;
if meta.len() < 100_000_000 {
    anyhow::bail!("GGUF at {} is {} bytes — suspiciously small, re-download", model_path.display(), meta.len());
}

Try / catch

match Model::load_with(&model_path, &opts) {
    Ok(m) => Ok(m),
    Err(e) if !matches!(backend, Backend::Cpu) => {
        warn!("{backend:?} load failed ({e}); falling back to CPU");
        Model::load_with(&model_path, &ModelOptions { backend: Backend::Cpu, gpu_device: None })
    }
    Err(e) => Err(anyhow::anyhow!("Failed to load whisper model {model_id}: {e}")),
}

Prevention

When it happens

Trigger: Loading a GGUF that is corrupt (failed disk copy), too new for the bundled transcribe-cpp version, quantized in an unsupported format, or requesting a GPU backend whose driver/runtime fails to initialize (missing Vulkan ICD, outdated drivers, out of VRAM).

Common situations: Models copied between machines with interrupted transfers; newly published GGUF quants needing a newer llama.cpp-compatible revision; headless/Linux systems without Vulkan drivers; Windows GPU driver updates breaking loader init; 32-bit quant files on backends lacking support.

Related errors


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

Appendix: source

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

                        // valid exact device, backend selection handles the
                        // retired generic GPU state and host CPU guard.
                        let backend = if device.is_some() {
                            Backend::Auto
                        } else {
                            select_transcribe_backend(accelerator)
                        };
                        (backend, device)
                    }
                };
                let requested_device = device
                    .as_ref()
                    .map(transcribe_device_label)
                    .unwrap_or_else(|| "automatic".to_string());
                let model_options = ModelOptions { backend, device };
                let model = Model::load_with(&model_path, &model_options).map_err(|e| {
                    let error_msg = format!("Failed to load whisper model {}: {}", model_id, e);
                    emit_loading_failed(&error_msg);
                    anyhow::anyhow!(error_msg)
                })?;
                // The bound backend may differ from the request (e.g. CPU
                // fallback under Auto); log what actually loaded.
                let bound_backend = model.backend();
                let session = model.session().map_err(|e| {
                    let error_msg = format!(
                        "Failed to create session for whisper model {}: {}",
                        model_id, e
                    );
                    emit_loading_failed(&error_msg);
                    anyhow::anyhow!(error_msg)
                })?;
                // Reconcile the registry's advertised capabilities with the
                // loaded model's real ones (GGUF metadata) so badges/gating
                // reflect runtime truth, not the pre-download probe. The
                // load-completed event below triggers the frontend refresh.
                let caps = session.model().capabilities();
                self.model_manager.set_runtime_capabilities(

View on GitHub (pinned to ba10ce1943)