{"record":{"id":"6e407e27307d866b","repo":"mihomo-party-org/clash-party","slug":"http-response-status-response-statustext","errorCode":null,"errorMessage":"HTTP ${response.status} ${response.statusText}","messagePattern":"HTTP \\$\\{response\\.status\\} \\$\\{response\\.statusText\\}","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"src/main/utils/github.ts","lineNumber":152,"sourceCode":" * 下载 GitHub Release 资产\n * @param url 下载 URL\n * @param outputPath 输出路径\n */\nasync function downloadGitHubAsset(url: string, outputPath: string): Promise<void> {\n  const { githubProxy = '' } = await getAppConfig()\n  const urls = buildDownloadUrls(url, githubProxy)\n  let lastError: unknown\n  for (const candidate of urls) {\n    try {\n      log.debug(`Downloading asset from ${candidate}`)\n      const response = await chromeRequest.get(candidate, {\n        responseType: 'arraybuffer',\n        timeout: 30000\n      })\n      // chromeRequest 对任何状态码都 resolve，所以这里必须自己判断。否则镜像返回的\n      // 404/502 错误页会被当成下载成功写进内核文件，而且直接 return、不再尝试后续镜像。\n      if (response.status < 200 || response.status >= 300) {\n        throw new Error(`HTTP ${response.status} ${response.statusText}`)\n      }\n      await writeFile(outputPath, Buffer.from(response.data as Buffer))\n      log.debug(`Successfully downloaded asset to ${outputPath}`)\n      return\n    } catch (error) {\n      log.warn(`Download failed from ${candidate}, trying next`, error)\n      lastError = error\n    }\n  }\n  log.error(`Failed to download asset from all sources`, lastError)\n  throw lastError instanceof Error\n    ? new Error(`Download error: ${lastError.message}`)\n    : new Error('Failed to download core file')\n}\n\n/**\n * 安装特定版本的 mihomo 核心\n * @param version 版本号","sourceCodeStart":134,"sourceCodeEnd":170,"githubUrl":"https://github.com/mihomo-party-org/clash-party/blob/911e090537acdf7c50bee1c3aebecc2ef119a8b5/src/main/utils/github.ts#L134-L170","documentation":"downloadGitHubAsset uses chromeRequest, which resolves for ANY HTTP status (it does not reject on 4xx/5xx). So this explicit check throws 'HTTP <status> <statusText>' for non-2xx responses. Without it, a mirror's 404/502 HTML error page would be silently written to the mihomo core binary. The code then logs a warning and tries the next mirror candidate.","triggerScenarios":"installMihomoCore -> downloadGitHubAsset where a mirror/CDN (or GitHub itself) returns 404 (asset/tag removed), 502/503 (mirror proxy down), or 429 (rate limited) for the release asset URL.","commonSituations":"Downloading an old mihomo version whose release assets were deleted; GitHub mirror (ghproxy-style) temporarily down or blocking; requesting a prerelease tag that has no packaged assets; network middleboxes returning error pages with 200 — handled separately since only non-2xx throws here.","solutions":["Retry the install — the code already iterates to the next mirror; persistent failure means all mirrors are affected.","Verify the release tag exists and has the expected asset on github.com/MetaCubeX/mihomo/releases.","Choose a different version, or wait until the mirror/CDN recovers if 5xx.","Check the requested version string for typos (an invalid tag yields 404 for every asset).","Inspect the response status in the log line 'Download failed from <candidate>, trying next' to distinguish 404 (wrong tag) from 5xx (mirror down)."],"exampleFix":"// before: old tag with removed assets fails on every mirror\nawait installSpecificMihomoCore('v1.18.0')\n// after: validate the tag resolves before downloading\nconst tags = await fetchMihomoTags()\nif (!tags.some((t) => t.name === 'v1.18.0')) {\n  throw new Error(`Version v1.18.0 not available; latest is ${tags[0]?.name}`)\n}\nawait installSpecificMihomoCore('v1.18.0')","handlingStrategy":"retry","validationCode":"// HEAD the asset URL first to see the status before downloading the body\nconst head = await fetch(assetUrl, { method: 'HEAD' })\nif (!head.ok) {\n  throw new Error(`Asset not downloadable: HTTP ${head.status}; pick another mirror or version`)\n}","typeGuard":null,"tryCatchPattern":"try {\n  await installSpecificMihomoCore(version)\n} catch (e) {\n  const m = String(e).match(/HTTP (\\d+)/)\n  if (m && (+m[1] === 404 || +m[1] >= 500)) {\n    await retryWithBackoff(() => installSpecificMihomoCore(version), { attempts: 3 })\n  } else {\n    throw e\n  }\n}","preventionTips":["Prefer official GitHub URLs over third-party mirrors when possible","Validate release tags exist (via the tags API) before constructing asset URLs","HEAD-check asset URLs before writing the body to disk","Retry with backoff across multiple mirrors; never write non-2xx bodies to files"],"tags":["network","http","download","mirror","github-releases"],"backgroundTag":"http-non-2xx-response","analyzedSha":"911e090537acdf7c50bee1c3aebecc2ef119a8b5","analyzedAt":"2026-08-30T13:00:49.174Z","schemaVersion":2},"datasetVersion":"2026-08-30T13:17:10.514Z"}