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
- 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.
- 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).
- Re-download the model from the catalog to rule out corruption (hash-verified on download).
- Try a different quantization/size variant of the same model — the format may be unsupported by the bundled transcribe-cpp build.
- 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
- Default to Auto/CPU-capable paths and only promise GPU when the driver stack is known good.
- Keep GPU drivers updated and verify Vulkan works (vulkaninfo) before selecting GPU backends.
- Only load hash-verified downloads — re-download instead of retrying a suspect GGUF.
- Check free memory against model size (plus compute buffers) before large loads.
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
- Device index ( ) cannot host a model
- Failed to create session for whisper model
- transcribe-cpp transcription failed
- get_available_accelerators panicked
- Failed to create SileroVad
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)