SubtitleEdit/subtitleedit · error · InvalidOperationException
Dictionary download failed: {url}
Error message
Dictionary download failed: {url} What it means
Thrown after a spell-check dictionary download via DownloadDictionary completes without throwing, but the resulting MemoryStream has zero bytes. The download service (SpellCheckDictionaryDownloadService) delegates to DownloadHelper.DownloadFileAsync, which can succeed with an empty body if the server returns HTTP 200 with no content or a redirect to an empty page. This guard catches the silent-empty-download case that would otherwise produce a corrupt dictionary file.
Source
Thrown at src/ui/Features/SpellCheck/GetDictionaries/GetDictionariesViewModel.cs:206
if (!Directory.Exists(folder))
{
Directory.CreateDirectory(folder);
}
var dicFiles = new List<string>();
for (var i = 0; i < files.Count; i++)
{
var url = files[i];
var fileIndex = i;
var fileProgress = new Progress<float>(p => progress.Report((fileIndex + p) / files.Count));
using var stream = new MemoryStream();
await _spellCheckDictionaryDownloadService.DownloadDictionary(stream, url, fileProgress, cancellationToken);
if (stream.Length == 0)
{
throw new InvalidOperationException($"Dictionary download failed: {url}");
}
stream.Position = 0;
if (IsHunspellFile(url))
{
var targetFileName = Path.Combine(folder, GetFileNameFromUrl(url));
await using (var fileStream = File.Create(targetFileName))
{
await stream.CopyToAsync(fileStream, cancellationToken);
}
if (targetFileName.EndsWith(".dic", StringComparison.OrdinalIgnoreCase))
{
dicFiles.Add(targetFileName);
}
}
elseView on GitHub (pinned to 17a9f07487)
Solutions
- Verify the failing URL in a browser or with curl -I to check the actual Content-Length and status code.
- Check the _spellCheckDictionaryDownloadService's HttpClient configuration for proxy settings or redirect behaviour.
- Confirm the dictionary URL list is current — if the host moved files, update the URL source.
- If behind a corporate proxy, ensure the proxy is not stripping response bodies for the dictionary host.
- Add logging inside DownloadHelper.DownloadFileAsync to capture the HTTP status code and Content-Length header for diagnosis.
Example fix
// before
using var stream = new MemoryStream();
await _spellCheckDictionaryDownloadService.DownloadDictionary(stream, url, fileProgress, cancellationToken);
if (stream.Length == 0)
{
throw new InvalidOperationException($"Dictionary download failed: {url}");
}
// after — include the HTTP status / content-length in the message for diagnosis
using var stream = new MemoryStream();
await _spellCheckDictionaryDownloadService.DownloadDictionary(stream, url, fileProgress, cancellationToken);
if (stream.Length == 0)
{
throw new InvalidOperationException(
$"Dictionary download produced 0 bytes from {url}. The URL may be stale, blocked by a proxy, or the server returned an empty response.");
} Defensive patterns
Strategy: validation
Validate before calling
// Validate URL reachability and headers before downloading
using var probeReq = new HttpRequestMessage(HttpMethod.Head, url);
using var probeResp = await httpClient.SendAsync(probeReq, cancellationToken);
if (!probeResp.IsSuccessStatusCode || probeResp.Content.Headers.ContentLength is 0)
{
// Skip or flag this dictionary URL before attempting the full download
continue;
} Try / catch
try { await downloadService.DownloadDictionary(stream, url, progress, ct); }
catch (InvalidOperationException ex) when (ex.Message.Contains("Dictionary download failed"))
{ /* log url, skip this dictionary, continue with remaining files */ } Prevention
- Validate each dictionary URL with a HEAD request before downloading to catch empty/stale endpoints.
- Maintain a curated, version-pinned URL list for dictionary sources.
- Log the HTTP status code and Content-Length inside DownloadHelper for post-mortem diagnosis.
When it happens
Trigger: The URL in the files list returns HTTP 200 with an empty response body, a 30x redirect chain lands on an empty page, a CDN edge serves a stale/empty object, or a proxy strips the body. The check fires only when stream.Length == 0 after a successful (non-throwing) DownloadDictionary call.
Common situations: Dictionary URL list is outdated and the host has removed the file but returns 200 with an HTML error page that DownloadHelper writes as empty; corporate proxy intercepts and returns an empty body; the URL is an HTTPS endpoint whose certificate changed and a middlebox returns an empty response; CDN misconfiguration serves a zero-length object.
Related errors
- The requested URL was not found: {url}
- Download incomplete: expected {totalBytes} bytes, received {
- Failed to download file after {maxRetries} attempts. URL: {u
- Downloaded yt-dlp ({assetName}) failed SHA-256 verification
- Too many bytes for CCData!
AI-assisted analysis of SubtitleEdit/subtitleedit@17a9f07487 (2026-08-13).
Data as JSON: /api/errors/851a7ac438ca28d1.
Report an issue: GitHub.