cjpais/Handy · error · anyhow::Error
Model not downloaded
Error message
Model not downloaded
What it means
Returned when the requested model exists in the catalog but is_downloaded is false — there is nothing on disk to load. Unlike the not-found case, this branch emits a 'loading_failed' model-state-changed event before returning, so the UI is informed. It is a sequencing error: load was requested before download completed (or after the files were removed).
Solutions
- Download the model first and wait for the download-completed event before calling load.
- Re-download via the model-selector if files were deleted externally.
- In UI flows, gate the 'use this model' action on the downloaded state rather than optimistic loading.
- If the flag disagrees with disk (shows downloaded but files gone), trigger a re-download or state refresh.
Example fix
// before
manager.load_model(model_id).await?; // errors if not downloaded
// after — ensure download state before loading
if !manager.get_model_info(model_id).unwrap().is_downloaded {
manager.download_model(model_id, None).await?;
}
manager.load_model(model_id).await?; Defensive patterns
Strategy: validation
Validate before calling
// Ensure download state before loading
let info = model_manager.get_model_info(model_id)
.ok_or_else(|| anyhow::anyhow!("unknown model id {model_id}"))?;
if !info.is_downloaded {
manager.download_model(model_id, None).await?; // wait for completion
}
manager.load_model(model_id).await?; Type guard
fn is_ready_to_load(info: &ModelInfo) -> bool {
info.is_downloaded
} Try / catch
match manager.load_model(model_id).await {
Ok(_) => Ok(()),
Err(e) if e.to_string() == "Model not downloaded" => {
manager.download_model(model_id, None).await?;
manager.load_model(model_id).await
}
Err(e) => Err(e),
} Prevention
- Sequence flows: download-completed event must arrive before load_model is called.
- Disable 'use model' actions in the UI until the downloaded flag is true.
- Listen for the model-state-changed loading_failed event to give users a re-download action instead of a dead end.
- Never remove model files outside the app's own remove flow.
When it happens
Trigger: Calling load_model immediately after triggering a download without awaiting completion; deleting model files externally while the catalog flag stays true, then a state refresh flips it; new install where a default model was configured but never fetched.
Common situations: First-run flows racing onboarding; users clearing the models cache folder; app data migration losing model files; settings restored from another machine.
Related errors
- Model not available
- Model not found
- Complete model directory not found
- Complete model file not found
- download incomplete: expected
AI-assisted analysis of cjpais/Handy@ba10ce1943 (2026-09-17).
Data as JSON: /api/errors/036b2185cf4c6de0.
Report an issue: GitHub.
Appendix: source
Thrown at src-tauri/src/managers/transcription.rs:535
// Every failure after loading starts must emit a terminal event so the
// frontend can never remain in its loading state.
let emit_loading_failed = |error_msg: &str| {
let _ = self.app_handle.emit(
"model-state-changed",
ModelStateEvent {
event_type: "loading_failed".to_string(),
model_id: Some(model_id.to_string()),
model_name: Some(model_info.name.clone()),
error: Some(error_msg.to_string()),
},
);
};
if !model_info.is_downloaded {
let error_msg = "Model not downloaded";
emit_loading_failed(error_msg);
return Err(anyhow::anyhow!(error_msg));
}
let model_path = self
.model_manager
.get_model_path(model_id)
.inspect_err(|error| emit_loading_failed(&error.to_string()))?;
// Drop the current engine BEFORE building the new one so transcribe-cpp
// frees the previous native context first — avoids holding two models at
// once (peak memory on large GGUFs). Clear the id too: if the new load
// fails, status should read "no loaded model", not the dropped engine.
{
let mut engine = self.lock_engine();
*engine = None;
}
{
let mut current_model = self.current_model_id.lock().unwrap();
*current_model = None;View on GitHub (pinned to ba10ce1943)