stablyai/orca · error · Error

GitHub request failed ${res.status} ${res.statusText}: ${bod

Error message

GitHub request failed ${res.status} ${res.statusText}: ${body.slice(0, 300)}

What it means

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.

Source

Thrown at config/scripts/create-draft-release.mjs:77

function githubHeaders(token) {
  return {
    Accept: 'application/vnd.github+json',
    Authorization: `Bearer ${token}`,
    'X-GitHub-Api-Version': API_VERSION
  }
}

async function githubJson(fetchImpl, url, token, options = {}) {
  const res = await fetchImpl(url, {
    ...options,
    headers: {
      ...githubHeaders(token),
      ...options.headers
    }
  })
  if (!res.ok) {
    const body = await res.text().catch(() => '')
    throw new Error(`GitHub request failed ${res.status} ${res.statusText}: ${body.slice(0, 300)}`)
  }
  return res.json()
}

async function fetchRepoReleases(repo, token, fetchImpl) {
  const releases = []
  for (let page = 1; ; page += 1) {
    const pageReleases = await githubJson(
      fetchImpl,
      `https://api.github.com/repos/${repo}/releases?per_page=100&page=${page}`,
      token
    )
    if (!Array.isArray(pageReleases)) {
      throw new Error(`GitHub releases response page ${page} for ${repo} was not an array`)
    }
    releases.push(...pageReleases)
    if (pageReleases.length < 100) {
      break

View on GitHub (pinned to 1136503c6a)

Solutions

  1. 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.
  2. For 429/5xx, retry with exponential backoff respecting the Retry-After / X-RateLimit-Reset headers.
  3. Confirm GH_TOKEN (or GITHUB_TOKEN) is set with repo and workflow scopes in the CI environment.

Example fix

// before: token missing scope -> 403
await createDraftRelease({ repo: 'owner/repo', tag: 'v1.0.0', token: weakToken })
// after: token with repo scope, plus retry wrapper for 5xx
await createDraftRelease({ repo: 'owner/repo', tag: 'v1.0.0', token: scopedToken })
Defensive patterns

Strategy: retry

Validate before calling

async function isGitHubReachable(token, fetchImpl = fetch) {
  const res = await fetchImpl('https://api.github.com/zen', {
    headers: { Authorization: `Bearer ${token}` }
  })
  return res.ok || res.status === 404 // 404 on /zen still means auth/network ok
}

Try / catch

async function createDraftReleaseWithRetry(params, maxAttempts = 3) {
  for (let attempt = 1; ; attempt += 1) {
    try {
      return await createDraftRelease(params)
    } catch (error) {
      const transient = /GitHub request failed (429|5\d\d)/.test(error.message)
      if (!transient || attempt >= maxAttempts) throw error
      await new Promise(r => setTimeout(r, 2 ** attempt * 1000))
    }
  }
}

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Related errors


AI-assisted analysis of stablyai/orca@1136503c6a (2026-08-12). Data as JSON: /api/errors/9e0c914f4eecc9c1. Report an issue: GitHub.