{"record":{"id":"5393f151cae0ec64","repo":"QuantumNous/new-api","slug":"failed-to-contact-github-releases-api","errorCode":null,"errorMessage":"Failed to contact GitHub releases API","messagePattern":"Failed to contact GitHub releases API","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"info","filePath":"web/src/features/system-settings/maintenance/update-checker-section.tsx","lineNumber":70,"sourceCode":"\n  const uptime = startTime ? formatTimestamp(startTime) : t('Unknown')\n  const version = currentVersion || t('Unknown')\n\n  const handleCheckUpdates = async () => {\n    setChecking(true)\n    try {\n      const response = await fetch(\n        'https://api.github.com/repos/Calcium-Ion/new-api/releases/latest',\n        {\n          headers: {\n            Accept: 'application/vnd.github+json',\n            'User-Agent': 'new-api-dashboard',\n          },\n        }\n      )\n\n      if (!response.ok) {\n        throw new Error(t('Failed to contact GitHub releases API'))\n      }\n\n      const data = (await response.json()) as ReleaseInfo\n      if (!data?.tag_name) {\n        throw new Error(t('Unexpected release payload'))\n      }\n\n      if (currentVersion && data.tag_name === currentVersion) {\n        toast.success(\n          t('You are running the latest version ({{version}}).', {\n            version: data.tag_name,\n          })\n        )\n        return\n      }\n\n      setRelease(data)\n      setDialogOpen(true)","sourceCodeStart":52,"sourceCodeEnd":88,"githubUrl":"https://github.com/QuantumNous/new-api/blob/e2c7aa7b102c2075eae2377df3508658d45e88dc/web/src/features/system-settings/maintenance/update-checker-section.tsx#L52-L88","documentation":"Thrown by the update checker when fetch of the GitHub releases API (api.github.com/repos/Calcium-Ion/new-api/releases/latest) returns a non-OK HTTP status. This is a direct browser-to-GitHub fetch, not a backend proxy call, so the dashboard's origin is the client.","triggerScenarios":"GitHub rate limiting (60 req/h per IP unauthenticated, common behind shared NAT/corporate proxies), HTTP 403/429 from rate limits, 5xx from GitHub, corporate firewall/proxy blocking api.github.com, or DNS/TLS failure.","commonSituations":"Many users behind one egress IP trip the unauthenticated rate limit; air-gapped or China-mainland deployments where api.github.com is slow or blocked; ad-blocker/privacy extension interfering; GitHub outage.","solutions":["Retry after the rate-limit window (check X-RateLimit-Reset on the 403/429 response).","If api.github.com is blocked in the deployment region, proxy the check through the backend or a mirror domain instead of fetching from the browser.","Add a conditional/unauthenticated PAT via Authorization header to raise the rate limit if this is a frequently shared dashboard.","Confirm network egress and DNS from users' browsers; this fetch happens client-side, not server-side."],"exampleFix":"// before\nif (!response.ok) {\n  throw new Error(t('Failed to contact GitHub releases API'))\n}\n// after: surface rate-limit info explicitly\nif (!response.ok) {\n  const reset = response.headers.get('X-RateLimit-Reset')\n  throw new Error(\n    response.status === 403 || response.status === 429\n      ? t('GitHub API rate limited. Try again later.')\n      : t('Failed to contact GitHub releases API')\n  )\n}","handlingStrategy":"retry","validationCode":null,"typeGuard":null,"tryCatchPattern":"try {\n  const response = await fetch(url, { headers: { Accept: 'application/vnd.github+json', 'User-Agent': 'new-api-dashboard' } })\n  if (response.status === 403 || response.status === 429) {\n    // rate limited — back off, do not spam retries\n    throw new Error(t('GitHub API rate limited. Try again later.'))\n  }\n  if (!response.ok) throw new Error(t('Failed to contact GitHub releases API'))\n  …\n} catch { /* network/CORS failure — show offline state, keep current version UI */ }","preventionTips":["Cache the last successful release check and only refetch on an explicit user action or long TTL.","For regions that block api.github.com, proxy the lookup through the backend where egress is controllable.","Treat update checks as best-effort: never block dashboard usage on their failure."],"tags":["update-checker","github-api","rate-limit","network"],"backgroundTag":null,"analyzedSha":"e2c7aa7b102c2075eae2377df3508658d45e88dc","analyzedAt":"2026-08-15T10:35:18.111Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}