{"record":{"id":"2ddc1f2d889e79e2","repo":"Budibase/budibase","slug":"github-response-response-status","errorCode":null,"errorMessage":"GitHub response: ${response.status}","messagePattern":"GitHub response: (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"packages/worker/src/api/controllers/global/github.ts","lineNumber":38,"sourceCode":"  const envelope = (await cache.get(CACHE_KEY, {\n    useTenancy: false,\n  })) as StarsCacheEnvelope | null\n  if (envelope && envelope.expiresAt > Date.now()) {\n    ctx.body = envelope.value\n    return\n  }\n\n  try {\n    const response = await fetch(GITHUB_REPO_URL, {\n      headers: {\n        Accept: \"application/vnd.github+json\",\n        \"User-Agent\": USER_AGENT,\n      },\n      timeout: GITHUB_TIMEOUT_MS,\n    })\n\n    if (!response.ok) {\n      throw new Error(`GitHub response: ${response.status}`)\n    }\n\n    const json = (await response.json()) as { stargazers_count?: number }\n    const stars = json.stargazers_count\n\n    if (typeof stars !== \"number\") {\n      throw new Error(\"GitHub stars missing\")\n    }\n\n    const value: GetGitHubStarsResponse = {\n      stars,\n      fetchedAt: new Date().toISOString(),\n    }\n    const toStore: StarsCacheEnvelope = {\n      value,\n      expiresAt: Date.now() + CACHE_TTL_MS,\n    }\n","sourceCodeStart":20,"sourceCodeEnd":56,"githubUrl":"https://github.com/Budibase/budibase/blob/a81a902e9a8fe55b467d106765f6638f12e35c49/packages/worker/src/api/controllers/global/github.ts#L20-L56","documentation":"getStars queries the public GitHub API for the Budibase repo's stargazers_count using fetch. Any non-ok HTTP response (rate limits, network errors surfacing as failure statuses, API downtime) triggers this generic error carrying only the status code.","triggerScenarios":"GitHub API returns 403 (rate limit without a token), 5xx outages, 404 if the repo path changed, or any other non-2xx response from the fetch call.","commonSituations":"Unauthenticated GitHub API rate-limiting on shared egress IPs (60 req/hour), CI environments with heavy outbound calls, temporary GitHub incidents.","solutions":["Check the status in the message: 403/429 means rate limiting — add a GITHUB_TOKEN or reduce call frequency","Add caching/backoff so the endpoint is called less often","Retry after the rate-limit window resets (see X-RateLimit-Reset response header)","Verify api.github.com status if 5xx; confirm the repo path is correct if 404"],"exampleFix":null,"handlingStrategy":"retry","validationCode":"// check rate-limit headroom before calling\nconst res = await fetch(\"https://api.github.com/rate_limit\")\nconst { remaining } = (await res.json()).resources.core\nif (remaining <= 0) throw new Error(\"GitHub rate limit exhausted\")","typeGuard":"function isGitHubOk(res: Response): boolean {\n  return res.ok && res.status >= 200 && res.status < 300\n}","tryCatchPattern":"try {\n  const stars = await getStars()\n} catch (err) {\n  if (err.message.startsWith(\"GitHub response: 403\")) {\n    // rate limited: back off / use a token; else fall back to cached value\n  }\n}","preventionTips":["Cache the stargazers value instead of calling GitHub per request","Use an authenticated GITHUB_TOKEN to raise the rate limit","Respect X-RateLimit-Reset and apply exponential backoff","Monitor GitHub API status for incidents causing 5xx responses"],"tags":["github","http","rate-limit","network"],"backgroundTag":"http-request-failed","analyzedSha":"a81a902e9a8fe55b467d106765f6638f12e35c49","analyzedAt":"2026-08-29T01:03:10.972Z","schemaVersion":2},"datasetVersion":"2026-08-29T02:17:18.158Z"}