Zackriya-Solutions/meetily · error · anyhow::Error

Failed to delete file '{}': {}

Error message

Failed to delete file '{}': {}

What it means

In delete_model's Corrupted branch, tokio fs::remove_file on the model path failed and the OS error is appended. The path in the message plus the errno identify the blocker: PermissionDenied (locked or read-only), NotFound (already gone), or IsADirectory. The status is not reset to Missing when this fires.

Source

Thrown at frontend/src-tauri/src/whisper_engine/whisper_engine.rs:853

        // Get model info to find the file path
        let model_info = {
            let models = self.available_models.read().await;
            models.get(model_name).cloned()
        };

        let model_info = model_info.ok_or_else(|| anyhow!("Model '{}' not found", model_name))?;

        // Check if model is corrupted before allowing deletion
        log::info!("Model '{}' has status: {:?}", model_name, model_info.status);
        match &model_info.status {
            ModelStatus::Corrupted { file_size, expected_min_size } => {
                log::info!("Deleting corrupted model '{}' (file size: {} bytes, expected min: {} bytes)",
                          model_name, file_size, expected_min_size);

                // Delete the file
                if model_info.path.exists() {
                    fs::remove_file(&model_info.path).await
                        .map_err(|e| anyhow!("Failed to delete file '{}': {}", model_info.path.display(), e))?;
                    log::info!("Successfully deleted corrupted file: {}", model_info.path.display());
                } else {
                    log::warn!("File '{}' does not exist, nothing to delete", model_info.path.display());
                }

                // Update model status to Missing
                {
                    let mut models = self.available_models.write().await;
                    if let Some(model) = models.get_mut(model_name) {
                        model.status = ModelStatus::Missing;
                    }
                }

                Ok(format!("Successfully deleted corrupted model '{}'", model_name))
            }
            ModelStatus::Available => {
                // Allow deletion of available models for testing/cleanup
                log::info!("Deleting available model '{}' (for cleanup)", model_name);

View on GitHub (pinned to 0281737d87)

Solutions

  1. Call unload_model before delete_model so no open file handle remains
  2. Close other app instances and wait a few seconds for antivirus scans to release the file, then retry
  3. Fix permissions on the models directory (remove read-only flag, verify ownership) and retry
  4. As a last resort, delete ggml-<name>.bin manually and rescan models
Defensive patterns

Strategy: retry

Validate before calling

// Unload first so no open handle remains, then delete
await invoke('unload_whisper_model');
await invoke('delete_model', { modelName });

Try / catch

for (let attempt = 1; attempt <= 3; attempt++) {
  try {
    await invoke('delete_model', { modelName });
    break;
  } catch (e) {
    if (String(e).includes('Failed to delete file') && attempt < 3) {
      await sleep(1000 * attempt); // AV/lock usually clears; retry
    } else { throw e; }
  }
}

Prevention

When it happens

Trigger: Windows file lock: another process (antivirus, a still-loaded whisper context, second app instance) holds an open handle on the ggml file; read-only file permissions; models dir on a volume unmounted mid-operation.

Common situations: Windows delete-after-load flows where the model context was never unloaded; corporate machines with AV scanning new .bin files; models stored on external/network drives.

Related errors


AI-assisted analysis of Zackriya-Solutions/meetily@0281737d87 (2026-08-16). Data as JSON: /api/errors/a730a64c9445bde0. Report an issue: GitHub.