cjpais/Handy · error · anyhow::Error

Complete model file not found

Error message

Complete model file not found: {}

What it means

Returned by ModelManager::get_model_path() for file-based models when neither a complete model file nor a state without a matching .partial download exists — i.e., there is nothing on disk to load for the given model_id. It is the 'the model you asked for is not present locally' error. Directory-based models have a sibling 'Complete model directory not found' variant just above.

Solutions

  1. Download the model first via the model manager / model-selector UI before requesting its path.
  2. Check is_downloaded (or list models) to confirm on-disk state instead of assuming the catalog reflects disk.
  3. If a .partial file lingers from an interrupted download, resume or restart the download — do not delete the partial if resume is desired.
  4. Verify the app's model data directory was not moved or cleaned by an external tool (disk cleaners are a common culprit).

Example fix

// before
let path = model_manager.get_model_path(model_id)?;

// after — gate on download state and recover
if !model_manager.get_model_info(model_id).map(|i| i.is_downloaded).unwrap_or(false) {
    model_manager.download_model(model_id, None).await?;
}
let path = model_manager.get_model_path(model_id)?;
Defensive patterns

Strategy: validation

Validate before calling

// Check on-disk state before asking for a path
let info = model_manager.get_model_info(model_id)
    .ok_or_else(|| anyhow::anyhow!("unknown model id {model_id}"))?;
if !info.is_downloaded {
    model_manager.download_model(model_id, None).await?;
}
let path = model_manager.get_model_path(model_id)?;

Type guard

fn model_is_ready(m: &ModelInfo) -> bool {
    m.is_downloaded // catalog flag mirrors a verified on-disk file
}

Try / catch

match model_manager.get_model_path(model_id) {
    Ok(p) => p,
    Err(e) if e.to_string().contains("Complete model") => {
        // nothing on disk: download then retry
        model_manager.download_model(model_id, None).await?;
        model_manager.get_model_path(model_id)?
    }
    Err(e) => return Err(e),
}

Prevention

When it happens

Trigger: Calling get_model_path(model_id) when the model was never downloaded, a previous download left only a .partial file, the models directory was moved/cleaned, or files were deleted externally while the catalog still marks the model as downloaded.

Common situations: Asking transcription to load a model right after a fresh install without downloading; users clearing app data / model cache folders; a download interrupted by app exit leaving only partial files; settings pointing at a model removed in a newer catalog.

Related errors


AI-assisted analysis of cjpais/Handy@ba10ce1943 (2026-09-17). Data as JSON: /api/errors/26c78c9520e1dd3a. Report an issue: GitHub.

Appendix: source

Thrown at src-tauri/src/managers/model.rs:2627

        if model_info.is_directory {
            if !model_path.exists() || !model_path.is_dir() {
                self.mark_model_unavailable(model_id);
                return Err(anyhow::anyhow!(
                    "Complete model directory not found: {}",
                    model_id
                ));
            }
            if partial_path.exists() {
                return Err(anyhow::anyhow!(
                    "Model directory is incomplete: {}",
                    model_id
                ));
            }
            Ok(model_path)
        } else {
            if !model_path.exists() {
                self.mark_model_unavailable(model_id);
                return Err(anyhow::anyhow!(
                    "Complete model file not found: {}",
                    model_id
                ));
            }
            if partial_path.exists() {
                return Err(anyhow::anyhow!("Model file is incomplete: {}", model_id));
            }
            Ok(model_path)
        }
    }

    pub fn cancel_download(&self, model_id: &str) -> Result<()> {
        debug!("ModelManager: cancel_download called for: {}", model_id);

        // Trigger the cancellation token to stop the download. The HF path
        // aborts its in-flight chunk tasks and unwinds promptly; the URL path
        // observes it on the next chunk of its stream loop.
        {

View on GitHub (pinned to ba10ce1943)