{"record":{"id":"fecbf27a3eaf6756","repo":"cjpais/Handy","slug":"no-download-source-for-model","errorCode":null,"errorMessage":"No download source for model","messagePattern":"No download source for model","errorType":"exception","errorClass":"anyhow::Error","httpStatus":null,"severity":"error","filePath":"src-tauri/src/managers/model.rs","lineNumber":2215,"sourceCode":"\n    pub async fn download_model(&self, model_id: &str) -> Result<()> {\n        let model_info = {\n            let models = self.available_models.lock().unwrap();\n            models.get(model_id).cloned()\n        };\n\n        let model_info =\n            model_info.ok_or_else(|| anyhow::anyhow!(\"Model not found: {}\", model_id))?;\n\n        let (url, expected_sha256) = match &model_info.source {\n            ModelSource::Url { url, sha256 } => (url.clone(), sha256.clone()),\n            ModelSource::HuggingFace { repo_id, revision } => {\n                return self\n                    .download_hf_model(&model_info, repo_id.clone(), revision.clone())\n                    .await;\n            }\n            ModelSource::Local => {\n                return Err(anyhow::anyhow!(\"No download source for model\"));\n            }\n        };\n        let model_path = self.models_dir.join(&model_info.filename);\n        let partial_path = self\n            .models_dir\n            .join(format!(\"{}.partial\", &model_info.filename));\n\n        // Don't download if complete version already exists\n        if model_path.exists() {\n            // Clean up any partial file that might exist\n            if partial_path.exists() {\n                let _ = fs::remove_file(&partial_path);\n            }\n            self.update_download_status()?;\n            return Ok(());\n        }\n\n        // Mark as downloading","sourceCodeStart":2197,"sourceCodeEnd":2233,"githubUrl":"https://github.com/cjpais/Handy/blob/ba10ce1943ef34e93c09494027fc0b9ced2e8a44/src-tauri/src/managers/model.rs#L2197-L2233","documentation":"The model entry's source is ModelSource::Local — files bundled with the app or placed by the user, with no URL and no HuggingFace repo — so download_model has nothing to fetch and refuses immediately. This is a usage error: the download pipeline only handles ModelSource::Url and ModelSource::HuggingFace.","triggerScenarios":"UI enabling the Download button for a Local or is_custom model; code paths that call download_model unconditionally for every catalog entry; a custom import incorrectly tagged as Local.","commonSituations":"Frontend not filtering by download availability; a custom model added via file-picker whose only 'download' option should be re-import; automation scripts iterating all models without checking source.","solutions":["Hide or disable the Download action for entries whose source is Local (or is_custom)","For custom models, direct users to re-import or re-place the files manually","As a caller, match on the model source before calling download_model"],"exampleFix":"// before (TS) — download offered for every model\n<button onClick={() => downloadModel(m.id)}>Download</button>\n\n// after — only offer download when a remote source exists\n{m.source?.type === \"url\" || m.source?.type === \"hugging_face\" ? (\n  <button onClick={() => downloadModel(m.id)}>Download</button>\n) : null}","handlingStrategy":"validation","validationCode":"// only download models that actually have a remote source\nif let Some(info) = manager.get_model_info(model_id) {\n    match info.source {\n        ModelSource::Url { .. } | ModelSource::HuggingFace { .. } => {\n            manager.download_model(model_id).await?;\n        }\n        ModelSource::Local => { /* hide download action for local/custom models */ }\n    }\n}","typeGuard":null,"tryCatchPattern":"match manager.download_model(id).await {\n    Err(e) if e.to_string().contains(\"No download source\") => {\n        // usage bug: Local model offered a download — fix the UI, don't retry\n        log::warn!(\"download requested for local model {id}\");\n    }\n    other => other?,\n}","preventionTips":["Filter the UI's download button on source type, not just on is_downloaded","Treat is_custom entries as manually-managed: import/remove instead of download"],"tags":["rust","model-source","local-model","unsupported-operation"],"backgroundTag":"unsupported-operation","analyzedSha":"ba10ce1943ef34e93c09494027fc0b9ced2e8a44","analyzedAt":"2026-08-17T10:29:55.597Z","contentChangedAt":"2026-08-17T10:29:55.597Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}