babalae/better-genshin-impact · warning · TimeoutException
图片下载超时({DownloadTimeout.TotalSeconds:0} 秒)。
Error message
图片下载超时({DownloadTimeout.TotalSeconds:0} 秒)。 What it means
Thrown when the banner image HTTP download does not complete within the hard-coded DownloadTimeout (30 seconds). The service links a CancellationTokenSource(DownloadTimeout) to the caller's token; if the timeout token fires but the caller's does not, the resulting OperationCanceledException is re-wrapped as TimeoutException so callers can distinguish a user-cancel from a network stall.
Source
Thrown at BetterGenshinImpact/Service/BannerImageService.cs:153
lock (_fileCommitLock)
{
if (operationId != Volatile.Read(ref _latestOperationId))
{
return false;
}
linkedCancellationToken.ThrowIfCancellationRequested();
// 保存图片到本地
File.Move(tempPath, NetworkImagePath, true);
}
return true;
}
catch (OperationCanceledException ex) when (
timeoutCancellationTokenSource.IsCancellationRequested && !cancellationToken.IsCancellationRequested)
{
throw new TimeoutException($"图片下载超时({DownloadTimeout.TotalSeconds:0} 秒)。", ex);
}
finally
{
TryDeleteFile(tempPath);
}
}
public void InvalidatePendingDownloads()
{
Interlocked.Increment(ref _latestOperationId);
}
public void ResetNetworkImage()
{
InvalidatePendingDownloads();
lock (_fileCommitLock)
{
if (File.Exists(UrlConfigPath))View on GitHub (pinned to a7cb36712d)
Solutions
- Use a smaller or better-hosted banner image URL so the full download finishes well under 30s.
- Verify network connectivity and proxy settings — a stalled TCP connection is the most common cause.
- If a longer deadline is acceptable, raise the DownloadTimeout constant in BannerImageService.cs (currently 30s) to fit the expected image size and bandwidth.
- Pre-compress the image and host it on a fast CDN.
Example fix
// before private static readonly TimeSpan DownloadTimeout = TimeSpan.FromSeconds(30); // after (if larger images are expected) private static readonly TimeSpan DownloadTimeout = TimeSpan.FromSeconds(60);
Defensive patterns
Strategy: try-catch
Validate before calling
// Validate reachability and expected size before committing to the timed download.
using var probe = await _httpClient.HeadAsync(uri, cancellationToken);
probe.EnsureSuccessStatusCode();
if (probe.Content.Headers.ContentLength is long len && len > MaxDownloadBytes)
{
throw new InvalidDataException($"图片大小超过 {MaxDownloadBytes / 1024 / 1024} MB 限制。");
} Try / catch
try
{
await bannerImageService.DownloadAndSaveAsync(url, cancellationToken);
}
catch (TimeoutException ex)
{
// Surface a user-facing message; the inner OperationCanceledException is preserved.
logger.LogWarning(ex, "Banner download timed out for {Url}", url);
} Prevention
- Prefer small, fast-hosted banner images that download well under 30s.
- If the network is known to be slow, raise DownloadTimeout to a value proportional to MaxDownloadBytes / expected bandwidth.
- Issue a HEAD request first to reject oversized or unreachable URLs before starting the timed download.
When it happens
Trigger: Calling BannerImageService.DownloadAndSaveAsync against a slow or stalled server. HttpClient.GetAsync with HttpCompletionOption.ResponseHeadersRead blocks past 30s on header arrival, or the streaming ReadAsync loop stalls mid-body (connection alive, no bytes flowing). The linked token cancels and the `when` filter converts it.
Common situations: User sets a large remote banner URL behind a slow CDN or proxy; transient network congestion; server rate-limiting; image host geo-blocked causing TCP stalls.
Understand the failure class
- Timeouts: ETIMEDOUT, deadlines, and hung requests — what actually expires when a request times out.
Related errors
- 下载失败
- 下载内容不是有效的图片。
- Telegram API request timed out. Check your internet connecti
- 下载语言文件失败:{cultureName}.json
- milliseconds 不能小于 0
AI-assisted analysis of babalae/better-genshin-impact@a7cb36712d (2026-08-13).
Data as JSON: /api/errors/59f23ae24874d2fb.
Report an issue: GitHub.