{"record":{"id":"91a76ed1fb96ee0b","repo":"SubtitleEdit/subtitleedit","slug":"llama-cpp-label-download-failed-integrity-check","errorCode":null,"errorMessage":"llama.cpp {label} download failed integrity check (expected SHA-256 {expected}, got {actual}).","messagePattern":"llama\\.cpp (.+?) download failed integrity check \\(expected SHA-256 (.+?), got (.+?)\\)\\.","errorType":"exception","errorClass":"IOException","httpStatus":null,"severity":"error","filePath":"src/ui/Logic/Download/LlamaCppDownloadService.cs","lineNumber":65,"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                $\"llama.cpp {label} download failed integrity check (expected SHA-256 {expected}, got {actual}).\");\n        }\n    }\n\n    public async Task DownloadCudaRuntime(Stream stream, string variant, IProgress<float>? progress, CancellationToken cancellationToken)\n    {\n        // The two CUDA builds need different redistributables (cudart64_12.dll vs cudart64_13.dll),\n        // so the runtime archive has to follow whichever engine variant was picked.\n        var isCuda13 = variant == VariantCuda13;\n        var url = BaseUrl + (isCuda13 ? \"cudart-llama-bin-win-cuda-13.3-x64.zip\" : \"cudart-llama-bin-win-cuda-12.4-x64.zip\");\n        var key = isCuda13 ? DownloadHashManager.LlamaCpp.WindowsCuda13Runtime : DownloadHashManager.LlamaCpp.WindowsCudaRuntime;\n\n        await DownloadHelper.DownloadFileAsync(httpClient, url, stream, progress, cancellationToken);\n        await VerifyArchive(stream, key, \"CUDA runtime\", cancellationToken);\n    }\n\n    public async Task DownloadModel(string url, string destinationFileName, IProgress<float>? progress, CancellationToken cancellationToken)\n    {","sourceCodeStart":47,"sourceCodeEnd":83,"githubUrl":"https://github.com/SubtitleEdit/subtitleedit/blob/17a9f0748781032255db3526b7215d2fb891e3af/src/ui/Logic/Download/LlamaCppDownloadService.cs#L47-L83","documentation":"Thrown by LlamaCppDownloadService's integrity check. The expected hash is fetched from DownloadHashManager.GetLatestKnownHash(key); an empty/null value skips verification. When set, the SHA-256 of the stream is compared case-insensitively, and a mismatch raises IOException so the pipeline treats it as an I/O fault. The stream is rewound to Position 0 before and after hashing so downstream use continues from the start.","triggerScenarios":"A non-empty known hash exists for the llama.cpp download key, and the actual SHA-256 of the stream differs: corrupt/truncated download, wrong artifact served by mirror, MITM/proxy alteration, or the manager's rolling hash advanced to a new build while the cached bytes are from the old one.","commonSituations":"Rolling hash update in DownloadHashManager after a new llama.cpp release, but local cache still holds the prior binary; partial download flagged success; AV/quarantine altered bytes; CDN edge served stale artifact.","solutions":["Clear the cached llama.cpp download so the service re-fetches bytes matching the manager's current hash.","Verify DownloadHashManager.GetLatestKnownHash(key) corresponds to the artifact upstream is currently serving; correct the manager if a rolling update mis-fired.","Re-download via a clean network path / alternate mirror to rule out corruption in transit.","Confirm no AV/sync process modifies the file while it is being hashed."],"exampleFix":"// before\nawait llamaService.DownloadEngine(stream, variant, progress, ct);\n\n// after: force fresh download to match rolling hash\nstream.SetLength(0);\nawait llamaService.DownloadEngine(stream, variant, progress, ct);","handlingStrategy":"try-catch","validationCode":"var expected = DownloadHashManager.GetLatestKnownHash(key);\nif (string.IsNullOrEmpty(expected)) { /* verification skipped */ }","typeGuard":null,"tryCatchPattern":"try { await llamaService.VerifyAsync(stream, label, key, ct); }\ncatch (IOException ex) when (ex.Message.Contains(\"failed integrity check\"))\n{ stream.SetLength(0); await llamaService.DownloadEngine(stream, variant, progress, ct); }","preventionTips":["When DownloadHashManager rolls a hash forward, invalidate the local llama.cpp cache.","Log the manager hash and actual hash together on mismatch.","Treat rolling-hash drift as a re-download trigger, not a security alarm."],"tags":["integrity","sha256","llama-cpp","download","corruption","rolling-hash"],"backgroundTag":null,"analyzedSha":"17a9f0748781032255db3526b7215d2fb891e3af","analyzedAt":"2026-08-13T18:11:43.374Z","schemaVersion":2},"datasetVersion":"2026-08-13T19:17:28.613Z"}