{"record":{"id":"036b2185cf4c6de0","repo":"cjpais/Handy","slug":"model-not-downloaded","errorCode":null,"errorMessage":"Model not downloaded","messagePattern":"Model not downloaded","errorType":"exception","errorClass":"anyhow::Error","httpStatus":null,"severity":"error","filePath":"src-tauri/src/managers/transcription.rs","lineNumber":535,"sourceCode":"\n        // Every failure after loading starts must emit a terminal event so the\n        // frontend can never remain in its loading state.\n        let emit_loading_failed = |error_msg: &str| {\n            let _ = self.app_handle.emit(\n                \"model-state-changed\",\n                ModelStateEvent {\n                    event_type: \"loading_failed\".to_string(),\n                    model_id: Some(model_id.to_string()),\n                    model_name: Some(model_info.name.clone()),\n                    error: Some(error_msg.to_string()),\n                },\n            );\n        };\n\n        if !model_info.is_downloaded {\n            let error_msg = \"Model not downloaded\";\n            emit_loading_failed(error_msg);\n            return Err(anyhow::anyhow!(error_msg));\n        }\n\n        let model_path = self\n            .model_manager\n            .get_model_path(model_id)\n            .inspect_err(|error| emit_loading_failed(&error.to_string()))?;\n\n        // Drop the current engine BEFORE building the new one so transcribe-cpp\n        // frees the previous native context first — avoids holding two models at\n        // once (peak memory on large GGUFs). Clear the id too: if the new load\n        // fails, status should read \"no loaded model\", not the dropped engine.\n        {\n            let mut engine = self.lock_engine();\n            *engine = None;\n        }\n        {\n            let mut current_model = self.current_model_id.lock().unwrap();\n            *current_model = None;","sourceCodeStart":517,"sourceCodeEnd":553,"githubUrl":"https://github.com/cjpais/Handy/blob/ba10ce1943ef34e93c09494027fc0b9ced2e8a44/src-tauri/src/managers/transcription.rs#L517-L553","documentation":"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).","triggerScenarios":"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.","commonSituations":"First-run flows racing onboarding; users clearing the models cache folder; app data migration losing model files; settings restored from another machine.","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."],"exampleFix":"// before\nmanager.load_model(model_id).await?; // errors if not downloaded\n\n// after — ensure download state before loading\nif !manager.get_model_info(model_id).unwrap().is_downloaded {\n    manager.download_model(model_id, None).await?;\n}\nmanager.load_model(model_id).await?;","handlingStrategy":"validation","validationCode":"// Ensure download state before loading\nlet info = model_manager.get_model_info(model_id)\n    .ok_or_else(|| anyhow::anyhow!(\"unknown model id {model_id}\"))?;\nif !info.is_downloaded {\n    manager.download_model(model_id, None).await?; // wait for completion\n}\nmanager.load_model(model_id).await?;","typeGuard":"fn is_ready_to_load(info: &ModelInfo) -> bool {\n    info.is_downloaded\n}","tryCatchPattern":"match manager.load_model(model_id).await {\n    Ok(_) => Ok(()),\n    Err(e) if e.to_string() == \"Model not downloaded\" => {\n        manager.download_model(model_id, None).await?;\n        manager.load_model(model_id).await\n    }\n    Err(e) => Err(e),\n}","preventionTips":["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."],"tags":["model","download-state","transcription","sequencing"],"backgroundTag":"model-not-downloaded","analyzedSha":"ba10ce1943ef34e93c09494027fc0b9ced2e8a44","analyzedAt":"2026-09-17T15:09:05.215Z","contentChangedAt":"2026-09-17T15:09:05.215Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}