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

  1. Hide or disable the Download action for entries whose source is Local (or is_custom)
  2. For custom models, direct users to re-import or re-place the files manually
  3. 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

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


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 downloading

View on GitHub (pinned to ba10ce1943)