cjpais/Handy · error · anyhow::Error
Failed to extract archive: {}
Error message
Failed to extract archive: {} What it means
tar Archive::unpack over a GzDecoder failed while extracting a downloaded directory-based model (.tar.gz). The handler cleans up the temp extraction dir, deletes the corrupt partial archive so the next attempt starts fresh (issue #858), removes the model from the extracting set, and emits model-extraction-failed before returning this error.
Source
Thrown at src-tauri/src/managers/model.rs:2285
let error_msg = format!("Failed to extract archive: {}", e);
// Clean up failed extraction
let _ = fs::remove_dir_all(&temp_extract_dir);
// Delete the corrupt partial file so the next download attempt starts fresh
// instead of resuming from a broken archive (issue #858).
let _ = fs::remove_file(&partial_path);
// Remove from extracting set
{
let mut extracting = self.extracting_models.lock().unwrap();
extracting.remove(model_id);
}
let _ = self.app_handle.emit(
"model-extraction-failed",
&serde_json::json!({
"model_id": model_id,
"error": error_msg
}),
);
anyhow::anyhow!(error_msg)
})?;
// Find the actual extracted directory (archive might have a nested structure)
let extracted_dirs: Vec<_> = fs::read_dir(&temp_extract_dir)?
.filter_map(|entry| entry.ok())
.filter(|entry| entry.file_type().map(|ft| ft.is_dir()).unwrap_or(false))
.collect();
if extracted_dirs.len() == 1 {
// Single directory extracted, move it to the final location
let source_dir = extracted_dirs[0].path();
if final_model_dir.exists() {
fs::remove_dir_all(&final_model_dir)?;
}
fs::rename(&source_dir, &final_model_dir)?;
// Clean up temp directory
let _ = fs::remove_dir_all(&temp_extract_dir);
} else {View on GitHub (pinned to 98a4d80cce)
Solutions
- Check free disk space — extraction needs the uncompressed size on top of the archive
- Verify write permissions on the models directory
- Retry the download: the corrupt partial is auto-deleted, so the next attempt re-downloads cleanly
- If it repeats, compare the source archive's sha256 manually to rule out server-side corruption
Defensive patterns
Strategy: retry
Validate before calling
// verify the archive hash before extraction when a digest is known
if let Some(expected) = &expected_sha256 {
let actual = sha256_file(&partial_path)?;
if &actual != expected {
std::fs::remove_file(&partial_path)?;
anyhow::bail!("archive hash mismatch — re-downloading");
}
} Try / catch
// extraction failure already deletes the corrupt partial; the safe retry is simply re-downloading
if let Err(e) = try_extract(...) {
warn!("extraction failed ({e}); partial removed — retrying download once");
manager.download_model(model_id).await?;
} Prevention
- Check free disk space for the uncompressed size before extracting (2-4x the .tar.gz)
- Keep sha256 verification enabled for catalog models — it stops corrupt archives before extraction
- Never resume from a partially-written archive; let the cleanup delete the partial
When it happens
Trigger: Truncated or corrupt archive reaching extraction (unexpected when sha256 verification is active for catalog models); disk full during unpack (archives unpack to several times their compressed size); missing write permission on the models directory; tar entries with metadata the unpacker rejects.
Common situations: Interrupted downloads resumed from a silently corrupted partial; small system disks during multi-GB model installs; permission changes in the app data dir; exotic archives with absolute paths or odd ownership.
Related errors
- threshold must be between 0.0 and 1.0
- Failed to create VAD: {e}
- Failed to create SileroVad: {}
- Failed to create AudioRecorder: {}
- Failed to resolve VAD path: {}
AI-assisted analysis of cjpais/Handy@98a4d80cce (2026-08-16).
Data as JSON: /api/errors/7c73d6e9bffaafe1.
Report an issue: GitHub.