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

Can only delete corrupted or available models. Model

Error message

Can only delete corrupted or available models. Model '{}' has status: {:?}

What it means

delete_model only handles ModelStatus::Corrupted and ModelStatus::Available; every other status falls to the catch-all arm and is rejected with the current status shown. This is a deliberate state-machine guard: you cannot delete what is Missing (nothing on disk), Downloading (an active task owns the file), or Error (recover by re-downloading instead).

Solutions

  1. For Downloading: cancel the download first (set the cancel flag / cancel_download command) or wait for it to finish, then delete
  2. For Missing: nothing to delete — refresh the model list; the action already succeeded earlier
  3. For Error: re-run download_model to overwrite the bad file instead of deleting, or remove the file manually and rescan
Defensive patterns

Strategy: validation

Validate before calling

const models = await invoke<ModelInfo[]>('get_whisper_models');
const m = models.find(x => x.name === modelName);
if (!m) throw new Error('Unknown model');
if (m.status !== 'Available' && m.status !== 'Corrupted') {
  throw new Error(`Cannot delete while status is ${m.status}. Cancel the download or refresh.`);
}
await invoke('delete_model', { modelName });

Type guard

const isDeletable = (s: string): boolean => s === 'Available' || s === 'Corrupted';

Try / catch

try {
  await invoke('delete_model', { modelName });
} catch (e) {
  if (String(e).includes('Can only delete')) {
    // Downloading -> cancel first; Missing -> refresh list; Error -> re-download instead
    refreshModelList();
  } else { throw e; }
}

Prevention

When it happens

Trigger: Calling delete_model while download_model streams the same model (Downloading); double-delete where the first call already set status Missing; calling delete on a model whose status is Error.

Common situations: User hits Delete during an active download; UI row not refreshed after a delete so the action fires twice; trying to 'clear' an Error state by deleting it.

Understand the failure class

Background: "Invalid state transition" errors: "status must be X, actually Y", "already rejected/charging/uninstalled", "cannot ... while running" — what they mean when a library rejects your call — this error's family across 31 libraries.

Related errors


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

Appendix: source

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

                    fs::remove_file(&model_info.path).await
                        .map_err(|e| anyhow!("Failed to delete file '{}': {}", model_info.path.display(), e))?;
                    log::info!("Successfully deleted available model 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 model '{}'", model_name))
            }
            _ => {
                Err(anyhow!("Can only delete corrupted or available models. Model '{}' has status: {:?}", model_name, model_info.status))
            }
        }
    }
    
    async fn reserve_active_download(&self, model_name: &str) -> Result<Arc<ActiveDownload>> {
        let mut active_downloads = self.active_downloads.lock().await;
        if active_downloads.contains_key(model_name) {
            return Err(anyhow!("Download already in progress for model: {}", model_name));
        }

        let (completion, _) = watch::channel(false);
        let active_download = Arc::new(ActiveDownload {
            cancellation: CancellationToken::new(),
            completion,
        });

        active_downloads.insert(model_name.to_string(), Arc::clone(&active_download));
        Ok(active_download)

View on GitHub (pinned to a2cb62e827)