{"record":{"id":"59f23ae24874d2fb","repo":"babalae/better-genshin-impact","slug":"downloadtimeout-totalseconds-0","errorCode":null,"errorMessage":"图片下载超时（{DownloadTimeout.TotalSeconds:0} 秒）。","messagePattern":"图片下载超时（(.+?) 秒）。","errorType":"exception","errorClass":"TimeoutException","httpStatus":null,"severity":"warning","filePath":"BetterGenshinImpact/Service/BannerImageService.cs","lineNumber":153,"sourceCode":"\n            lock (_fileCommitLock)\n            {\n                if (operationId != Volatile.Read(ref _latestOperationId))\n                {\n                    return false;\n                }\n\n                linkedCancellationToken.ThrowIfCancellationRequested();\n                // 保存图片到本地\n                File.Move(tempPath, NetworkImagePath, true);\n            }\n\n            return true;\n        }\n        catch (OperationCanceledException ex) when (\n            timeoutCancellationTokenSource.IsCancellationRequested && !cancellationToken.IsCancellationRequested)\n        {\n            throw new TimeoutException($\"图片下载超时（{DownloadTimeout.TotalSeconds:0} 秒）。\", ex);\n        }\n        finally\n        {\n            TryDeleteFile(tempPath);\n        }\n    }\n\n    public void InvalidatePendingDownloads()\n    {\n        Interlocked.Increment(ref _latestOperationId);\n    }\n\n    public void ResetNetworkImage()\n    {\n        InvalidatePendingDownloads();\n        lock (_fileCommitLock)\n        {\n            if (File.Exists(UrlConfigPath))","sourceCodeStart":135,"sourceCodeEnd":171,"githubUrl":"https://github.com/babalae/better-genshin-impact/blob/a7cb36712dcb409be610257d877fcea3597e9d6b/BetterGenshinImpact/Service/BannerImageService.cs#L135-L171","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"// before\nprivate static readonly TimeSpan DownloadTimeout = TimeSpan.FromSeconds(30);\n\n// after (if larger images are expected)\nprivate static readonly TimeSpan DownloadTimeout = TimeSpan.FromSeconds(60);","handlingStrategy":"try-catch","validationCode":"// Validate reachability and expected size before committing to the timed download.\nusing var probe = await _httpClient.HeadAsync(uri, cancellationToken);\nprobe.EnsureSuccessStatusCode();\nif (probe.Content.Headers.ContentLength is long len && len > MaxDownloadBytes)\n{\n    throw new InvalidDataException($\"图片大小超过 {MaxDownloadBytes / 1024 / 1024} MB 限制。\");\n}","typeGuard":null,"tryCatchPattern":"try\n{\n    await bannerImageService.DownloadAndSaveAsync(url, cancellationToken);\n}\ncatch (TimeoutException ex)\n{\n    // Surface a user-facing message; the inner OperationCanceledException is preserved.\n    logger.LogWarning(ex, \"Banner download timed out for {Url}\", url);\n}","preventionTips":["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."],"tags":["network","download","timeout","configuration","banner-image"],"backgroundTag":null,"analyzedSha":"a7cb36712dcb409be610257d877fcea3597e9d6b","analyzedAt":"2026-08-13T16:44:57.548Z","schemaVersion":2},"datasetVersion":"2026-08-13T19:17:28.613Z"}