SubtitleEdit/subtitleedit · error · IOException
Qwen3 TTS {label} download failed integrity check (expected
Error message
Qwen3 TTS {label} download failed integrity check (expected SHA-256 {expected}, got {actual}). What it means
Integrity check for the Qwen3 TTS (cpp) engine download. Expected hash comes from DownloadHashManager.GetLatestKnownHash(key); the freshly downloaded stream is hashed in place (rewound to 0 before and after) and compared case-insensitively. Mismatch throws IOException.
Source
Thrown at src/ui/Logic/Download/Qwen3TtsCppDownloadService.cs:96
{
if (string.IsNullOrEmpty(key) || stream.Length == 0)
{
return;
}
var expected = DownloadHashManager.GetLatestKnownHash(key);
if (string.IsNullOrEmpty(expected))
{
return;
}
stream.Position = 0;
var actual = await Sha256Util.ComputeSha256Async(stream, cancellationToken);
stream.Position = 0;
if (!string.Equals(expected, actual, StringComparison.OrdinalIgnoreCase))
{
throw new IOException(
$"Qwen3 TTS {label} download failed integrity check (expected SHA-256 {expected}, got {actual}).");
}
}
public async Task DownloadModels(string modelsFolder, string ttsModelFileName, IProgress<float>? progress, Action<string>? titleProgress, CancellationToken cancellationToken)
{
if (!TtsModelUrls.TryGetValue(ttsModelFileName, out var ttsModelUrl))
{
throw new ArgumentException($"Unknown Qwen3 TTS model: {ttsModelFileName}", nameof(ttsModelFileName));
}
var ttsModelPath = Path.Combine(modelsFolder, ttsModelFileName);
var tokenizerPath = Path.Combine(modelsFolder, TokenizerModelFileName);
var needTts = !File.Exists(ttsModelPath);
var needTokenizer = !File.Exists(tokenizerPath);
var total = (needTts ? 1 : 0) + (needTokenizer ? 1 : 0);
var step = 0;
View on GitHub (pinned to 17a9f07487)
Solutions
- Refresh DownloadHashManager so the latest known hash matches the current artifact.
- Delete and re-download the affected file.
- Verify the engine artifact version the binary expects vs. the one the CDN serves.
- Check for truncation from network/disk.
Defensive patterns
Strategy: validation
Validate before calling
var expected = DownloadHashManager.GetLatestKnownHash(key);
if (!string.IsNullOrEmpty(expected))
{
stream.Position = 0;
var actual = await Sha256Util.ComputeSha256Async(stream, ct);
stream.Position = 0;
if (!string.Equals(expected, actual, StringComparison.OrdinalIgnoreCase))
{
await DownloadHashManager.RefreshAsync(ct);
}
} Try / catch
try { await service.DownloadAsync(...); }
catch (IOException ex) when (ex.Message.Contains("Qwen3 TTS") && ex.Message.Contains("integrity check"))
{
await DownloadHashManager.RefreshAsync(ct);
if (File.Exists(dest)) File.Delete(dest);
await service.DownloadAsync(...);
} Prevention
- Refresh the hash manager before validating freshly downloaded engine files.
- Always rewind the stream to 0 before hashing (the code does this; preserve it on edits).
- Delete cached engine files after a binary version bump.
When it happens
Trigger: A non-empty expected hash from the manager that differs from the SHA-256 of the downloaded stream. Empty expected skips the check.
Common situations: Stream not actually rewound before hashing elsewhere (here it is), a partial download, an updated origin artifact that the hash manager hasn't tracked yet, or a corrupt cache.
Related errors
- OmniVoice {label} download failed integrity check (expected
- Qwen3 TTS (CrispASR) model {fileName} failed integrity check
- MOSS-TTS (CrispASR) model {fileName} failed integrity check
- OmniVoice (CrispASR) model {fileName} failed integrity check
- VibeVoice (CrispASR) model {fileName} failed integrity check
AI-assisted analysis of SubtitleEdit/subtitleedit@17a9f07487 (2026-08-13).
Data as JSON: /api/errors/782881c9f34d2b9d.
Report an issue: GitHub.