{"record":{"id":"9e0c914f4eecc9c1","repo":"stablyai/orca","slug":"github-request-failed-res-status-res-statuste","errorCode":null,"errorMessage":"GitHub request failed ${res.status} ${res.statusText}: ${body.slice(0, 300)}","messagePattern":"GitHub request failed (.+?) (.+?): (.+?)","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"config/scripts/create-draft-release.mjs","lineNumber":77,"sourceCode":"function githubHeaders(token) {\n  return {\n    Accept: 'application/vnd.github+json',\n    Authorization: `Bearer ${token}`,\n    'X-GitHub-Api-Version': API_VERSION\n  }\n}\n\nasync function githubJson(fetchImpl, url, token, options = {}) {\n  const res = await fetchImpl(url, {\n    ...options,\n    headers: {\n      ...githubHeaders(token),\n      ...options.headers\n    }\n  })\n  if (!res.ok) {\n    const body = await res.text().catch(() => '')\n    throw new Error(`GitHub request failed ${res.status} ${res.statusText}: ${body.slice(0, 300)}`)\n  }\n  return res.json()\n}\n\nasync function fetchRepoReleases(repo, token, fetchImpl) {\n  const releases = []\n  for (let page = 1; ; page += 1) {\n    const pageReleases = await githubJson(\n      fetchImpl,\n      `https://api.github.com/repos/${repo}/releases?per_page=100&page=${page}`,\n      token\n    )\n    if (!Array.isArray(pageReleases)) {\n      throw new Error(`GitHub releases response page ${page} for ${repo} was not an array`)\n    }\n    releases.push(...pageReleases)\n    if (pageReleases.length < 100) {\n      break","sourceCodeStart":59,"sourceCodeEnd":95,"githubUrl":"https://github.com/stablyai/orca/blob/1136503c6a231a16dce8f921f6fadb63d181e8db/config/scripts/create-draft-release.mjs#L59-L95","documentation":"Thrown by githubJson when the GitHub API response status is not ok (res.ok === false). The error embeds the HTTP status, status text, and the first 300 chars of the response body for diagnosis (create-draft-release.mjs:67-80). It fires for any non-2xx response across releases listing, generate-notes, and release creation.","triggerScenarios":"401/403 from an invalid or expired token; 404 from a wrong repo slug or insufficient permissions; 422 from a malformed release body or duplicate tag; 429/secondary rate limit; 5xx transient GitHub errors.","commonSituations":"GH_TOKEN/GITHUB_TOKEN not set or lacking repo scope in CI; GITHUB_REPOSITORY pointing to the wrong owner/repo; creating a draft for a tag that already has a non-draft release; release body exceeding limits after truncation; hitting the GitHub API rate limit in a long CI matrix.","solutions":["Read the embedded status/body: 401/403 means fix the token scope; 404 means fix the repo slug or token perms; 422 means inspect the body for the rejected field.","For 429/5xx, retry with exponential backoff respecting the Retry-After / X-RateLimit-Reset headers.","Confirm GH_TOKEN (or GITHUB_TOKEN) is set with repo and workflow scopes in the CI environment."],"exampleFix":"// before: token missing scope -> 403\nawait createDraftRelease({ repo: 'owner/repo', tag: 'v1.0.0', token: weakToken })\n// after: token with repo scope, plus retry wrapper for 5xx\nawait createDraftRelease({ repo: 'owner/repo', tag: 'v1.0.0', token: scopedToken })","handlingStrategy":"retry","validationCode":"async function isGitHubReachable(token, fetchImpl = fetch) {\n  const res = await fetchImpl('https://api.github.com/zen', {\n    headers: { Authorization: `Bearer ${token}` }\n  })\n  return res.ok || res.status === 404 // 404 on /zen still means auth/network ok\n}","typeGuard":null,"tryCatchPattern":"async function createDraftReleaseWithRetry(params, maxAttempts = 3) {\n  for (let attempt = 1; ; attempt += 1) {\n    try {\n      return await createDraftRelease(params)\n    } catch (error) {\n      const transient = /GitHub request failed (429|5\\d\\d)/.test(error.message)\n      if (!transient || attempt >= maxAttempts) throw error\n      await new Promise(r => setTimeout(r, 2 ** attempt * 1000))\n    }\n  }\n}","preventionTips":["Inject GH_TOKEN/GITHUB_TOKEN via CI secrets and verify scope before the release step.","Wrap createDraftRelease in retry-with-backoff for 429/5xx, reading Retry-After when present.","For 4xx, surface the embedded body to diagnose token/repo/body issues without retrying."],"tags":["network","github-api","release"],"backgroundTag":null,"analyzedSha":"1136503c6a231a16dce8f921f6fadb63d181e8db","analyzedAt":"2026-08-12T23:15:58.167Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}