{"record":{"id":"70d60e231a8212b6","repo":"SubtitleEdit/subtitleedit","slug":"omnivoice-label-download-failed-integrity-check","errorCode":null,"errorMessage":"OmniVoice {label} download failed integrity check (expected SHA-256 {expected}, got {actual}).","messagePattern":"OmniVoice (.+?) download failed integrity check \\(expected SHA-256 (.+?), got (.+?)\\)\\.","errorType":"exception","errorClass":"IOException","httpStatus":null,"severity":"error","filePath":"src/ui/Logic/Download/OmniVoiceDownloadService.cs","lineNumber":111,"sourceCode":"    {\n        if (string.IsNullOrEmpty(key) || stream.Length == 0)\n        {\n            return;\n        }\n\n        var expected = DownloadHashManager.GetLatestKnownHash(key);\n        if (string.IsNullOrEmpty(expected))\n        {\n            return;\n        }\n\n        stream.Position = 0;\n        var actual = await Sha256Util.ComputeSha256Async(stream, cancellationToken);\n        stream.Position = 0;\n\n        if (!string.Equals(expected, actual, StringComparison.OrdinalIgnoreCase))\n        {\n            throw new IOException(\n                $\"OmniVoice {label} download failed integrity check (expected SHA-256 {expected}, got {actual}).\");\n        }\n    }\n\n    private static string GetUrl(string windowsVariant)\n    {\n        if (OperatingSystem.IsWindows())\n        {\n            return windowsVariant switch\n            {\n                WindowsVariantCuda => WindowsCudaUrl,\n                WindowsVariantVulkan => WindowsVulkanUrl,\n                _ => WindowsCpuUrl,\n            };\n        }\n\n        if (OperatingSystem.IsMacOS())\n        {","sourceCodeStart":93,"sourceCodeEnd":129,"githubUrl":"https://github.com/SubtitleEdit/subtitleedit/blob/17a9f0748781032255db3526b7215d2fb891e3af/src/ui/Logic/Download/OmniVoiceDownloadService.cs#L93-L129","documentation":"OmniVoice engine (not the CrispASR model) integrity check. Unlike the CrispASR variants, the expected hash is fetched dynamically from DownloadHashManager.GetLatestKnownHash(key), allowing the hash to be updated without rebuilding. Mismatch throws IOException.","triggerScenarios":"After download the stream is rewound to position 0, hashed, rewound again, and compared against the manager's latest known hash. A non-empty expected hash that differs from actual triggers the throw.","commonSituations":"The hash manager holds a stale hash (update channel not refreshed) while the origin already serves a newer build; a partial download; a download cache that survived a version bump in the binary but not in the hash manager.","solutions":["Force a refresh of DownloadHashManager so it pulls the latest known hash for the key.","Delete and re-download the affected file.","Confirm the origin artifact and the hash-manager entry agree on the same version.","Check network/disk for truncation."],"exampleFix":null,"handlingStrategy":"validation","validationCode":"var expected = DownloadHashManager.GetLatestKnownHash(key);\nif (!string.IsNullOrEmpty(expected))\n{\n    stream.Position = 0;\n    var actual = await Sha256Util.ComputeSha256Async(stream, ct);\n    stream.Position = 0;\n    if (!string.Equals(expected, actual, StringComparison.OrdinalIgnoreCase))\n    {\n        // refresh the hash manager, then re-evaluate before failing the user\n        await DownloadHashManager.RefreshAsync(ct);\n        expected = DownloadHashManager.GetLatestKnownHash(key);\n    }\n}","typeGuard":null,"tryCatchPattern":"try { await service.DownloadAsync(...); }\ncatch (IOException ex) when (ex.Message.Contains(\"OmniVoice\") && ex.Message.Contains(\"integrity check\"))\n{\n    await DownloadHashManager.RefreshAsync(ct);\n    if (File.Exists(dest)) File.Delete(dest);\n    await service.DownloadAsync(...);\n}","preventionTips":["Refresh DownloadHashManager on startup so expected hashes track the latest artifacts.","Delete cached downloads after upgrading to a new engine version.","Log both expected and actual hashes on mismatch to speed diagnosis."],"tags":["integrity-check","sha256","download","omnivoice","hash-manager","csharp"],"backgroundTag":null,"analyzedSha":"17a9f0748781032255db3526b7215d2fb891e3af","analyzedAt":"2026-08-13T18:11:43.374Z","schemaVersion":2},"datasetVersion":"2026-08-13T19:17:28.613Z"}