babalae/better-genshin-impact · error · Exception

下载失败

Error message

下载失败

What it means

Thrown by DownloadRepoAndUnzipCore when the HTTP GET to the repo download URL returns a non-success status code. The response body is not read; only IsSuccessStatusCode is checked. Any 4xx/5xx (or a redirect that HttpClient doesn't follow) triggers this.

Source

Thrown at BetterGenshinImpact/Core/Script/ScriptRepoUpdater.cs:2070

    {
        await _repoWriteLock.WaitAsync();
        try
        {
            await DownloadRepoAndUnzipCore(url);
        }
        finally
        {
            _repoWriteLock.Release();
        }
    }

    private async Task DownloadRepoAndUnzipCore(string url)
    {
        // 下载
        var res = await _httpClient.GetAsync(url);
        if (!res.IsSuccessStatusCode)
        {
            throw new Exception("下载失败");
        }

        var bytes = await res.Content.ReadAsByteArrayAsync();

        // 获取文件名
        var contentDisposition = res.Content.Headers.ContentDisposition;
        var fileName = contentDisposition is { FileName: not null }
            ? contentDisposition.FileName.Trim('"')
            : "temp.zip";

        // 创建临时目录
        if (!Directory.Exists(ReposTempPath))
        {
            Directory.CreateDirectory(ReposTempPath);
        }

        // 保存下载的文件
        var zipPath = Path.Combine(ReposTempPath, fileName);

View on GitHub (pinned to a7cb36712d)

Solutions

  1. Include the status code in the error: read res.StatusCode and res.ReasonPhrase before throwing.
  2. Retry with exponential backoff for transient 5xx/429 responses (the HttpClient has a 30s timeout but no retry).
  3. Verify the URL in repo.json is reachable: open it in a browser or curl -I.
  4. For GitHub release assets, ensure the URL is the resolved asset download URL, not the API URL that returns JSON.

Example fix

// before
var res = await _httpClient.GetAsync(url);
if (!res.IsSuccessStatusCode)
    throw new Exception("下载失败");

// after (include status + body hint)
var res = await _httpClient.GetAsync(url);
if (!res.IsSuccessStatusCode)
{
    var body = await res.Content.ReadAsStringAsync();
    throw new HttpRequestException(
        $"下载失败: {(int)res.StatusCode} {res.ReasonPhrase}。URL={url}。响应: {body?.Take(200)}");
}
Defensive patterns

Strategy: retry

Validate before calling

// Optional pre-check: HEAD the URL
using var probe = await _httpClient.SendAsync(new HttpRequestMessage(HttpMethod.Head, url));
if (!probe.IsSuccessStatusCode)
    throw new HttpRequestException($"下载URL不可用: {(int)probe.StatusCode}");

Try / catch

// Retry transient failures with backoff, then surface the status
int attempt = 0;
HttpResponseMessage res;
do {
    res = await _httpClient.GetAsync(url);
    if (res.IsSuccessStatusCode) break;
    if ((int)res.StatusCode is >= 500 or 429 && attempt < 3)
        await Task.Delay(TimeSpan.FromSeconds(1 << attempt));
    else break;
} while (++attempt <= 3);
if (!res.IsSuccessStatusCode)
    throw new HttpRequestException($"下载失败: {(int)res.StatusCode} {res.ReasonPhrase}");

Prevention

When it happens

Trigger: _httpClient.GetAsync(url) returns a HttpResponseMessage where IsSuccessStatusCode is false — e.g., 404 (wrong URL), 401/403 (auth needed), 5xx (server error), or a timeout surfaced as a status (though HttpClient.Timeout actually throws TaskCanceledException instead).

Common situations: The download URL in repo.json is stale or wrong; the hosting service (e.g., GitHub release assets) moved/removed the file; rate limiting returned 403/429; a proxy returned an error page; the server requires an auth header not configured on the shared HttpClient.

Related errors


AI-assisted analysis of babalae/better-genshin-impact@a7cb36712d (2026-08-13). Data as JSON: /api/errors/100bf1c09ca75ae9. Report an issue: GitHub.