cjpais/Handy · error · anyhow::Error
No download source for model
Error message
No download source for model
What it means
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.
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
Example fix
// before (TS) — download offered for every model
<button onClick={() => downloadModel(m.id)}>Download</button>
// after — only offer download when a remote source exists
{m.source?.type === "url" || m.source?.type === "hugging_face" ? (
<button onClick={() => downloadModel(m.id)}>Download</button>
) : null} Defensive patterns
Strategy: validation
Validate before calling
// only download models that actually have a remote source
if let Some(info) = manager.get_model_info(model_id) {
match info.source {
ModelSource::Url { .. } | ModelSource::HuggingFace { .. } => {
manager.download_model(model_id).await?;
}
ModelSource::Local => { /* hide download action for local/custom models */ }
}
} Try / catch
match manager.download_model(id).await {
Err(e) if e.to_string().contains("No download source") => {
// usage bug: Local model offered a download — fix the UI, don't retry
log::warn!("download requested for local model {id}");
}
other => other?,
} Prevention
- 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
When it happens
Trigger: 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.
Common situations: 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.
Understand the failure class
Background: UnsupportedOperationException and "is not supported" errors: when a library deliberately refuses a call — this error's family across 30 libraries.
Related errors
- Canary transcription failed
- Cohere transcription failed
- Complete model directory not found
- Complete model file not found in HF cache or models dir
- Device index ( ) cannot host a model
AI-assisted analysis of cjpais/Handy@ba10ce1943 (2026-08-17).
Data as JSON: /api/errors/fecbf27a3eaf6756.
Report an issue: GitHub.
Appendix: source
Thrown at src-tauri/src/managers/model.rs:2215
pub async fn download_model(&self, model_id: &str) -> Result<()> {
let model_info = {
let models = self.available_models.lock().unwrap();
models.get(model_id).cloned()
};
let model_info =
model_info.ok_or_else(|| anyhow::anyhow!("Model not found: {}", model_id))?;
let (url, expected_sha256) = match &model_info.source {
ModelSource::Url { url, sha256 } => (url.clone(), sha256.clone()),
ModelSource::HuggingFace { repo_id, revision } => {
return self
.download_hf_model(&model_info, repo_id.clone(), revision.clone())
.await;
}
ModelSource::Local => {
return Err(anyhow::anyhow!("No download source for model"));
}
};
let model_path = self.models_dir.join(&model_info.filename);
let partial_path = self
.models_dir
.join(format!("{}.partial", &model_info.filename));
// Don't download if complete version already exists
if model_path.exists() {
// Clean up any partial file that might exist
if partial_path.exists() {
let _ = fs::remove_file(&partial_path);
}
self.update_download_status()?;
return Ok(());
}
// Mark as downloadingView on GitHub (pinned to ba10ce1943)