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

  1. Check free disk space — extraction needs the uncompressed size on top of the archive
  2. Verify write permissions on the models directory
  3. Retry the download: the corrupt partial is auto-deleted, so the next attempt re-downloads cleanly
  4. 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

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


AI-assisted analysis of cjpais/Handy@98a4d80cce (2026-08-16). Data as JSON: /api/errors/7c73d6e9bffaafe1. Report an issue: GitHub.