{"record":{"id":"7c73d6e9bffaafe1","repo":"cjpais/Handy","slug":"failed-to-extract-archive","errorCode":null,"errorMessage":"Failed to extract archive: {}","messagePattern":"Failed to extract archive: (.+?)","errorType":"exception","errorClass":"anyhow::Error","httpStatus":null,"severity":"error","filePath":"src-tauri/src/managers/model.rs","lineNumber":2285,"sourceCode":"                let error_msg = format!(\"Failed to extract archive: {}\", e);\n                // Clean up failed extraction\n                let _ = fs::remove_dir_all(&temp_extract_dir);\n                // Delete the corrupt partial file so the next download attempt starts fresh\n                // instead of resuming from a broken archive (issue #858).\n                let _ = fs::remove_file(&partial_path);\n                // Remove from extracting set\n                {\n                    let mut extracting = self.extracting_models.lock().unwrap();\n                    extracting.remove(model_id);\n                }\n                let _ = self.app_handle.emit(\n                    \"model-extraction-failed\",\n                    &serde_json::json!({\n                        \"model_id\": model_id,\n                        \"error\": error_msg\n                    }),\n                );\n                anyhow::anyhow!(error_msg)\n            })?;\n\n            // Find the actual extracted directory (archive might have a nested structure)\n            let extracted_dirs: Vec<_> = fs::read_dir(&temp_extract_dir)?\n                .filter_map(|entry| entry.ok())\n                .filter(|entry| entry.file_type().map(|ft| ft.is_dir()).unwrap_or(false))\n                .collect();\n\n            if extracted_dirs.len() == 1 {\n                // Single directory extracted, move it to the final location\n                let source_dir = extracted_dirs[0].path();\n                if final_model_dir.exists() {\n                    fs::remove_dir_all(&final_model_dir)?;\n                }\n                fs::rename(&source_dir, &final_model_dir)?;\n                // Clean up temp directory\n                let _ = fs::remove_dir_all(&temp_extract_dir);\n            } else {","sourceCodeStart":2267,"sourceCodeEnd":2303,"githubUrl":"https://github.com/cjpais/Handy/blob/98a4d80cce8ad41efec2a419b59d9e81229a35d7/src-tauri/src/managers/model.rs#L2267-L2303","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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"],"exampleFix":null,"handlingStrategy":"retry","validationCode":"// verify the archive hash before extraction when a digest is known\nif let Some(expected) = &expected_sha256 {\n    let actual = sha256_file(&partial_path)?;\n    if &actual != expected {\n        std::fs::remove_file(&partial_path)?;\n        anyhow::bail!(\"archive hash mismatch — re-downloading\");\n    }\n}","typeGuard":null,"tryCatchPattern":"// extraction failure already deletes the corrupt partial; the safe retry is simply re-downloading\nif let Err(e) = try_extract(...) {\n    warn!(\"extraction failed ({e}); partial removed — retrying download once\");\n    manager.download_model(model_id).await?;\n}","preventionTips":["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"],"tags":["rust","tar","archive","extraction","disk-space","corrupt-download"],"backgroundTag":"archive-extraction-failed","analyzedSha":"98a4d80cce8ad41efec2a419b59d9e81229a35d7","analyzedAt":"2026-08-16T20:58:09.966Z","schemaVersion":2},"datasetVersion":"2026-08-16T23:17:17.608Z"}