{"record":{"id":"906a65a668e802f2","repo":"iOfficeAI/AionUi","slug":"update-errors-toomanyredirects","errorCode":"update.errors.tooManyRedirects","errorMessage":"update.errors.tooManyRedirects","messagePattern":"update\\.errors\\.tooManyRedirects","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/desktop/src/process/bridge/updateBridge.ts","lineNumber":312,"sourceCode":"      redirect: 'manual',\n      headers: {\n        'User-Agent': DEFAULT_USER_AGENT,\n      },\n    });\n\n    if (res.status >= 300 && res.status < 400) {\n      const location = res.headers.get('location');\n      if (!location) {\n        throw new Error((await getI18n()).t('update.errors.redirectNoLocation'));\n      }\n      current = new URL(location, current).toString();\n      continue;\n    }\n\n    return res;\n  }\n\n  throw new Error((await getI18n()).t('update.errors.tooManyRedirects'));\n};\n\nconst fetchGitHubReleases = async (repo: string, timeoutMs = 30000): Promise<GitHubReleaseApi[]> => {\n  const url = `https://api.github.com/repos/${repo}/releases`;\n\n  // 添加超时控制，防止网络问题导致无限等待 / Add timeout to prevent infinite wait on network issues\n  const controller = new AbortController();\n  const timeoutId = setTimeout(() => controller.abort(), timeoutMs);\n\n  try {\n    const res = await fetch(url, {\n      headers: {\n        Accept: 'application/vnd.github+json',\n        'User-Agent': DEFAULT_USER_AGENT,\n      },\n      signal: controller.signal,\n    });\n","sourceCodeStart":294,"sourceCodeEnd":330,"githubUrl":"https://github.com/iOfficeAI/AionUi/blob/711aa0550ee183ea495dc33e2c05c7943b70a60a/packages/desktop/src/process/bridge/updateBridge.ts#L294-L330","documentation":"Thrown by fetchWithAllowlistedRedirects when the redirect loop exhausts its maximum iteration count — the fetch kept receiving 3xx responses (each with a valid Location) and never returned a final non-redirect response. This guards against redirect cycles.","triggerScenarios":"An update URL involved in a redirect loop (A -> B -> A) or an extremely long chain exceeding the loop cap; also a server that always answers 3xx regardless of request.","commonSituations":"CDN misroutes causing A/B bounce; auth cookies not forwarded so the server keeps redirecting to login; http->https plus trailing-slash redirects stacking into a cycle behind a proxy; stale presigned URLs that redirect forever.","solutions":["curl -IL the URL to trace the redirect chain and find the loop","Fix the server/proxy redirect rules (host canonicalization, trailing-slash handling)","Ensure any auth/cookie the endpoint needs is sent so it stops bouncing","Bypass the redirecting layer and point the updater at the final artifact URL"],"exampleFix":"# before\nurl = https://cdn.example.com/latest  # loops cdn <-> auth\n# after\nurl = https://github.com/owner/repo/releases/download/v1.2.0/app.dmg","handlingStrategy":"retry","validationCode":null,"typeGuard":null,"tryCatchPattern":"try {\n  await fetchWithAllowlistedRedirects(url, signal);\n} catch (err) {\n  if (err instanceof Error && err.message.includes('tooManyRedirects')) {\n    await backoff(); await fetchWithAllowlistedRedirects(url, signal); // one retry\n  } else throw err;\n}","preventionTips":["Point the updater at final artifact URLs rather than alias links","Keep server redirect rules acyclic (canonical host + trailing slash once)","Smoke-test feed URLs in CI with a redirect-tracing curl"],"tags":["http","redirect-loop","updater","network"],"backgroundTag":"too-many-redirects","analyzedSha":"711aa0550ee183ea495dc33e2c05c7943b70a60a","analyzedAt":"2026-08-28T07:56:06.558Z","schemaVersion":2},"datasetVersion":"2026-08-28T11:17:15.048Z"}