{"record":{"id":"403f957bf542ebff","repo":"cjpais/Handy","slug":"model-is-currently-downloading","errorCode":null,"errorMessage":"Model is currently downloading: {}","messagePattern":"Model is currently downloading: (.+?)","errorType":"exception","errorClass":"anyhow::Error","httpStatus":null,"severity":"warning","filePath":"src-tauri/src/managers/model.rs","lineNumber":2484,"sourceCode":"\n        // Emit event to notify UI\n        let _ = self.app_handle.emit(\"model-deleted\", model_id);\n\n        Ok(())\n    }\n\n    pub fn get_model_path(&self, model_id: &str) -> Result<PathBuf> {\n        let model_info = self\n            .get_model_info(model_id)\n            .ok_or_else(|| anyhow::anyhow!(\"Model not found: {}\", model_id))?;\n\n        if !model_info.is_downloaded {\n            return Err(anyhow::anyhow!(\"Model not available: {}\", model_id));\n        }\n\n        // Ensure we don't return partial files/directories\n        if model_info.is_downloading {\n            return Err(anyhow::anyhow!(\n                \"Model is currently downloading: {}\",\n                model_id\n            ));\n        }\n\n        if let ModelSource::HuggingFace { repo_id, revision } = &model_info.source {\n            if let Some(path) = hf_cached_path(repo_id, revision, &model_info.filename) {\n                return Ok(path);\n            }\n            // Mirror-fallback download or manual drop-in in the models dir.\n            // The complete file only ever appears after verification, so a\n            // stale `.partial` alongside it is leftover noise, not a veto —\n            // clear it rather than declaring the model missing.\n            let local_path = self.models_dir.join(&model_info.filename);\n            if local_path.exists() {\n                let partial_path = self\n                    .models_dir\n                    .join(format!(\"{}.partial\", &model_info.filename));","sourceCodeStart":2466,"sourceCodeEnd":2502,"githubUrl":"https://github.com/cjpais/Handy/blob/98a4d80cce8ad41efec2a419b59d9e81229a35d7/src-tauri/src/managers/model.rs#L2466-L2502","documentation":"Thrown by ModelManager::get_model_path when the model's registry entry has is_downloading=true. The manager refuses to hand out a path while a download task is active so callers never receive a half-written file; the guard also fires for partial files and directories. The main caller is TranscriptionManager::load_model, which calls get_model_path at transcription.rs:520.","triggerScenarios":"Calling get_model_path(model_id), or the transcription load path that wraps it, while download_model for the same id is still in flight — e.g. the user selects a model right after starting its download, or startup auto-load races an in-progress download.","commonSituations":"Race between the 'download started' state and a load request; a stuck is_downloading flag after the app was killed mid-download (the RAII cleanup guard in model.rs never ran); retry logic that immediately loads after enqueueing a download.","solutions":["Wait for the model-state-changed / download completion event before loading or calling get_model_path","Pre-check with get_model_info(model_id) and skip while is_downloading is true; show a 'downloading' UI state instead","If the flag is stale after a crash, restart the app (state is rebuilt from disk) or call cancel_download(model_id) to clear it, then retry","Run rescan_local_models to rebuild the registry's download state from the filesystem"],"exampleFix":"// before\nlet model_path = self.model_manager.get_model_path(model_id)?;\n\n// after\nlet info = self.model_manager.get_model_info(model_id)\n    .ok_or_else(|| anyhow::anyhow!(\"Model not found: {}\", model_id))?;\nif info.is_downloading {\n    // wait for the download-completed event instead of failing here\n    return Ok(());\n}\nlet model_path = self.model_manager.get_model_path(model_id)?;","handlingStrategy":"validation","validationCode":"let Some(info) = model_manager.get_model_info(model_id) else {\n    anyhow::bail!(\"Model not found: {}\", model_id);\n};\nif info.is_downloading {\n    // Skip loading now; subscribe to the model-state-changed event\n    // and retry once the download-completed event arrives.\n    return Ok(WaitOutcome::Downloading(info.partial_size));\n}\nlet path = model_manager.get_model_path(model_id)?;","typeGuard":"fn is_model_path_ready(mm: &ModelManager, id: &str) -> bool {\n    mm.get_model_info(id)\n        .map(|i| i.is_downloaded && !i.is_downloading)\n        .unwrap_or(false)\n}","tryCatchPattern":"match model_manager.get_model_path(model_id) {\n    Ok(path) => Ok(path),\n    Err(e) if e.to_string().starts_with(\"Model is currently downloading\") => {\n        // transient: wait for the download-completed event, then retry once\n        wait_for_model_event(\"download completed\", model_id).await?;\n        model_manager.get_model_path(model_id)\n    }\n    Err(e) => Err(e),\n}","preventionTips":["Gate model loading UI on get_model_info(id).is_downloading == false","Never call load_model immediately after download_model; await the completion event","After a crash, run rescan_local_models so stale is_downloading flags are rebuilt from disk"],"tags":["model-download","race-condition","state-flag","tauri"],"backgroundTag":"resource-in-use","analyzedSha":"98a4d80cce8ad41efec2a419b59d9e81229a35d7","analyzedAt":"2026-08-16T20:58:09.966Z","schemaVersion":2},"datasetVersion":"2026-08-16T23:17:17.608Z"}