cjpais/Handy · error · anyhow::Error

Model directory is incomplete

Error message

Model directory is incomplete: {}

What it means

Thrown by ModelManager::get_model_path when a directory-based model (is_directory == true, e.g. multi-file model bundles in the models dir) exists at its expected path, but a sibling `<filename>.partial` file is still present. The `.partial` marker is written during downloads and only removed after the download is fully verified, so its presence means the directory on disk is not a complete, verified model. The manager refuses to hand back a path that could point at half-written model data.

Solutions

  1. Delete the stale `<model_filename>.partial` file inside the models directory, then retry (if the model directory itself is complete, this is enough).
  2. If the directory contents are also suspect, delete the model directory entirely and re-download it via the app's model manager.
  3. Check the download is not actually still in progress (get_model_info / is_downloading) before touching files.
  4. Upgrade the app: newer code treats a complete model alongside a `.partial` as leftover noise and removes it automatically rather than erroring.

Example fix

// before: stale marker blocks a good directory
ls models_dir
# my-model-dir/  my-model-dir.partial
rm models_dir/my-model-dir.partial
// after: get_model_path returns the directory
Defensive patterns

Strategy: validation

Validate before calling

use std::path::Path;
fn model_dir_complete(models_dir: &Path, filename: &str) -> bool {
    let dir = models_dir.join(filename);
    dir.is_dir() && !models_dir.join(format!("{}.partial", filename)).exists()
}
// call before get_model_path/load_model; if false, clean up or re-download

Type guard

fn is_complete_model_dir(p: &Path) -> bool {
    p.is_dir() && !p.with_file_name(format!("{}.partial", p.file_name().map(|s| s.to_string_lossy().to_string()).unwrap_or_default())).exists()
}

Try / catch

match manager.get_model_path(id) {
    Ok(p) => p,
    Err(e) if e.to_string().contains("incomplete") => {
        // remove stale <file>.partial, then retry once
        let partial = models_dir.join(format!("{}.partial", filename));
        let _ = std::fs::remove_file(&partial);
        manager.get_model_path(id).context("model still incomplete after cleanup")?
    }
    Err(e) => return Err(e),
}

Prevention

When it happens

Trigger: Calling get_model_path(model_id) (directly or via load_model/load_model_with_device) for a directory-based model when: (1) a previous download of that model was interrupted (crash, kill, power loss) after the directory was created but before the `.partial` file was cleaned up; (2) a download is still running in another thread while is_downloading was somehow false; (3) a `.partial` file was left behind by an older app version with a different download scheme.

Common situations: User force-quits the app mid-download and later restarts; system crash or disk-full during a model download; manually copying a model folder into models/ while leaving a stale `.partial` beside it; restoring a partially-synced models directory from a backup or cloud-sync tool.

Understand the failure class

Background: "already exists" / EEXIST / FileAlreadyExistsException: what the 'file already exists' error means and how to fix it — this error's family across 37 libraries.

Related errors


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

Appendix: source

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

                model_id
            ));
        }

        let model_path = self.models_dir.join(&model_info.filename);
        let partial_path = self
            .models_dir
            .join(format!("{}.partial", &model_info.filename));

        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)
        }

View on GitHub (pinned to ba10ce1943)