{"record":{"id":"81e8802b41875290","repo":"iOfficeAI/AionUi","slug":"update-errors-githubapifailed","errorCode":"update.errors.githubApiFailed","errorMessage":"update.errors.githubApiFailed","messagePattern":"update\\.errors\\.githubApiFailed","errorType":"http","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/desktop/src/process/bridge/updateBridge.ts","lineNumber":332,"sourceCode":"\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\n    if (!res.ok) {\n      throw new Error((await getI18n()).t('update.errors.githubApiFailed', { status: res.status }));\n    }\n\n    const json = (await res.json()) as unknown;\n    if (!Array.isArray(json)) {\n      throw new Error((await getI18n()).t('update.errors.githubApiNotArray'));\n    }\n    return json as GitHubReleaseApi[];\n  } catch (err: unknown) {\n    if (err instanceof Error && err.name === 'AbortError') {\n      throw new Error((await getI18n()).t('update.errors.githubApiTimeout'), { cause: err });\n    }\n    throw err;\n  } finally {\n    clearTimeout(timeoutId);\n  }\n};\n\nconst CDN_MANIFEST_TIMEOUT_MS = 15000;","sourceCodeStart":314,"sourceCodeEnd":350,"githubUrl":"https://github.com/iOfficeAI/AionUi/blob/711aa0550ee183ea495dc33e2c05c7943b70a60a/packages/desktop/src/process/bridge/updateBridge.ts#L314-L350","documentation":"Thrown by fetchGitHubReleases when the GitHub Releases API call to https://api.github.com/repos/{repo}/releases returns a non-OK HTTP status. The status code is interpolated into the message, and the request carries a User-Agent and a 30s AbortController timeout.","triggerScenarios":"Calling fetchGitHubReleases for a repo that doesn't exist (404), hitting the rate limit (403/429), bad credentials (401), or a 5xx from GitHub. The message includes the numeric status so the cause is immediately readable.","commonSituations":"Unauthenticated GitHub API rate limiting (60 req/hr per IP) in CI or shared offices; typo'd owner/repo string; private repo without a token; GitHub incidents returning 5xx; proxy/firewall intercepting api.github.com.","solutions":["Read the {status} in the message: 403/429 => rate limited, 404 => wrong repo slug, 401 => auth","Fix the repo string if 404 ('owner/repo' with correct casing/spelling)","For rate limits, wait or add a GITHUB_TOKEN-capable header and cache release results","Check https://www.githubstatus.com for 5xx outages","Verify network/proxy allows api.github.com over HTTPS"],"exampleFix":"// before\nconst releases = await fetchGitHubReleases('aionui/aionui ');\n\n// after\nconst releases = await fetchGitHubReleases('aionui/aionui'.trim());","handlingStrategy":"try-catch","validationCode":"// cheap pre-check that the repo slug is well-formed\nconst REPO_RE = /^[\\w.-]+\\/[\\w.-]+$/;\nif (!REPO_RE.test(repo)) throw new Error('bad repo slug');\nawait fetchGitHubReleases(repo);","typeGuard":"const isRepoSlug = (s: string): boolean => /^[\\w.-]+\\/[\\w.-]+$/.test(s.trim());","tryCatchPattern":"try {\n  const releases = await fetchGitHubReleases(repo);\n} catch (err) {\n  if (err instanceof Error && err.message.includes('githubApiFailed')) {\n    const status = /* extract from message */ Number(err.message.match(/\\b(\\d{3})\\b/)?.[1]);\n    if (status === 403 || status === 429) await sleep(60_000); // rate limited, backoff\n    else if (status === 404) throw new Error('repo not found');\n    else await sleep(5_000);\n  } else throw err;\n}","preventionTips":["Cache GitHub release responses to stay under rate limits","Validate repo slug format before calling","Add an auth token header for higher limits in CI"],"tags":["github-api","http-status","updater","rate-limit"],"backgroundTag":"github-api-request-failed","analyzedSha":"711aa0550ee183ea495dc33e2c05c7943b70a60a","analyzedAt":"2026-08-28T07:56:06.558Z","schemaVersion":2},"datasetVersion":"2026-08-28T11:17:15.048Z"}