{"record":{"id":"100bf1c09ca75ae9","repo":"babalae/better-genshin-impact","slug":"error-100bf1","errorCode":null,"errorMessage":"下载失败","messagePattern":"下载失败","errorType":"exception","errorClass":"Exception","httpStatus":null,"severity":"error","filePath":"BetterGenshinImpact/Core/Script/ScriptRepoUpdater.cs","lineNumber":2070,"sourceCode":"    {\n        await _repoWriteLock.WaitAsync();\n        try\n        {\n            await DownloadRepoAndUnzipCore(url);\n        }\n        finally\n        {\n            _repoWriteLock.Release();\n        }\n    }\n\n    private async Task DownloadRepoAndUnzipCore(string url)\n    {\n        // 下载\n        var res = await _httpClient.GetAsync(url);\n        if (!res.IsSuccessStatusCode)\n        {\n            throw new Exception(\"下载失败\");\n        }\n\n        var bytes = await res.Content.ReadAsByteArrayAsync();\n\n        // 获取文件名\n        var contentDisposition = res.Content.Headers.ContentDisposition;\n        var fileName = contentDisposition is { FileName: not null }\n            ? contentDisposition.FileName.Trim('\"')\n            : \"temp.zip\";\n\n        // 创建临时目录\n        if (!Directory.Exists(ReposTempPath))\n        {\n            Directory.CreateDirectory(ReposTempPath);\n        }\n\n        // 保存下载的文件\n        var zipPath = Path.Combine(ReposTempPath, fileName);","sourceCodeStart":2052,"sourceCodeEnd":2088,"githubUrl":"https://github.com/babalae/better-genshin-impact/blob/a7cb36712dcb409be610257d877fcea3597e9d6b/BetterGenshinImpact/Core/Script/ScriptRepoUpdater.cs#L2052-L2088","documentation":"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.","triggerScenarios":"_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).","commonSituations":"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.","solutions":["Include the status code in the error: read res.StatusCode and res.ReasonPhrase before throwing.","Retry with exponential backoff for transient 5xx/429 responses (the HttpClient has a 30s timeout but no retry).","Verify the URL in repo.json is reachable: open it in a browser or curl -I.","For GitHub release assets, ensure the URL is the resolved asset download URL, not the API URL that returns JSON."],"exampleFix":"// before\nvar res = await _httpClient.GetAsync(url);\nif (!res.IsSuccessStatusCode)\n    throw new Exception(\"下载失败\");\n\n// after (include status + body hint)\nvar res = await _httpClient.GetAsync(url);\nif (!res.IsSuccessStatusCode)\n{\n    var body = await res.Content.ReadAsStringAsync();\n    throw new HttpRequestException(\n        $\"下载失败: {(int)res.StatusCode} {res.ReasonPhrase}。URL={url}。响应: {body?.Take(200)}\");\n}","handlingStrategy":"retry","validationCode":"// Optional pre-check: HEAD the URL\nusing var probe = await _httpClient.SendAsync(new HttpRequestMessage(HttpMethod.Head, url));\nif (!probe.IsSuccessStatusCode)\n    throw new HttpRequestException($\"下载URL不可用: {(int)probe.StatusCode}\");","typeGuard":null,"tryCatchPattern":"// Retry transient failures with backoff, then surface the status\nint attempt = 0;\nHttpResponseMessage res;\ndo {\n    res = await _httpClient.GetAsync(url);\n    if (res.IsSuccessStatusCode) break;\n    if ((int)res.StatusCode is >= 500 or 429 && attempt < 3)\n        await Task.Delay(TimeSpan.FromSeconds(1 << attempt));\n    else break;\n} while (++attempt <= 3);\nif (!res.IsSuccessStatusCode)\n    throw new HttpRequestException($\"下载失败: {(int)res.StatusCode} {res.ReasonPhrase}\");","preventionTips":["Always include status code + URL in the error.","Retry 5xx/429 with exponential backoff.","Verify repo.json's 'url'/'file' fields point at a live, resolvable asset."],"tags":["http","network","download","status-code","script-repo"],"backgroundTag":null,"analyzedSha":"a7cb36712dcb409be610257d877fcea3597e9d6b","analyzedAt":"2026-08-13T16:44:57.548Z","schemaVersion":2},"datasetVersion":"2026-08-13T19:17:28.613Z"}