{"record":{"id":"339f753fdd96cddc","repo":"cjpais/Handy","slug":"sha256-task-panicked","errorCode":null,"errorMessage":"SHA256 task panicked: {}","messagePattern":"SHA256 task panicked: (.+?)","errorType":"exception","errorClass":"anyhow::Error","httpStatus":null,"severity":"error","filePath":"src-tauri/src/managers/model/download.rs","lineNumber":117,"sourceCode":"        Ok(format!(\"{:x}\", hasher.finalize()))\n    }\n\n    /// Emit verification events around a blocking sha256 check of `path`.\n    /// On mismatch `verify_sha256` deletes the file, so the next attempt (or\n    /// next source) starts clean. A `None` hash skips checking (custom models).\n    async fn verify_file_with_events(\n        model_id: &str,\n        path: &Path,\n        expected_sha256: Option<&str>,\n        emit: &(dyn Fn(HttpDownloadEvent<'_>) + Send + Sync),\n    ) -> Result<()> {\n        emit(HttpDownloadEvent::VerificationStarted);\n        let path = path.to_path_buf();\n        let expected = expected_sha256.map(str::to_string);\n        let id = model_id.to_string();\n        tokio::task::spawn_blocking(move || Self::verify_sha256(&path, expected.as_deref(), &id))\n            .await\n            .map_err(|e| anyhow::anyhow!(\"SHA256 task panicked: {}\", e))??;\n        emit(HttpDownloadEvent::VerificationCompleted);\n        Ok(())\n    }\n\n    /// [`Self::download_http_resumable_with_events`] wired to the Tauri event\n    /// bus — the production entry point.\n    pub(super) async fn download_http_resumable(\n        &self,\n        model_id: &str,\n        url: &str,\n        partial_path: &Path,\n        expected_size: Option<u64>,\n        expected_sha256: Option<&str>,\n        cancel_token: &CancellationToken,\n    ) -> Result<HttpDownloadOutcome> {\n        let app_handle = self.app_handle.clone();\n        let id = model_id.to_string();\n        Self::download_http_resumable_with_events(","sourceCodeStart":99,"sourceCodeEnd":135,"githubUrl":"https://github.com/cjpais/Handy/blob/98a4d80cce8ad41efec2a419b59d9e81229a35d7/src-tauri/src/managers/model/download.rs#L99-L135","documentation":"verify_file_with_events runs the SHA256 check inside tokio::task::spawn_blocking; this error wraps the JoinError returned when that blocking task panicked or was cancelled instead of yielding a Result. The JoinError details are embedded, so the underlying panic can be identified from logs.","triggerScenarios":"A panic inside verify_sha256 on unexpected file state; the tokio runtime shutting down (app quit) while verification of a multi-GB file is in flight; blocking-pool task aborted at runtime teardown.","commonSituations":"Quitting the app during the final verification phase of a large model download; edge-case bugs in the hashing path; forced process termination during spawn_blocking work.","solutions":["Inspect the embedded JoinError payload in logs to identify the underlying panic; report it upstream if it is a code bug","Retry the download — a runtime shutdown leaves no corrupt committed state","Avoid killing the app during verification; use cancel_download for a clean stop"],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"let result = tokio::task::spawn_blocking(move || verify_sha256(&path, expected, &id)).await;\nmatch result {\n    Ok(inner) => inner?,                         // real verification result\n    Err(join_err) => {\n        // task panicked or was cancelled at shutdown — safe to retry once\n        warn!(\"SHA256 task failed: {}\", join_err);\n        if !app_shutting_down() { retry_verification().await?; }\n        anyhow::bail!(\"SHA256 task panicked: {}\", join_err);\n    }\n}","preventionTips":["Avoid killing the app during the verification phase — use cancel_download instead","Log the JoinError payload; a recurring panic is a code bug worth reporting","Keep the tokio runtime alive until in-flight spawn_blocking downloads finish"],"tags":["tokio","spawn-blocking","panic","sha256"],"backgroundTag":"async-task-panic","analyzedSha":"98a4d80cce8ad41efec2a419b59d9e81229a35d7","analyzedAt":"2026-08-16T20:58:09.966Z","schemaVersion":2},"datasetVersion":"2026-08-16T23:17:17.608Z"}