stablyai/orca · 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 the release asset verification script's githubFetch helper when a GitHub API HTTP response has a non-ok status (!res.ok). The error includes the HTTP status code, status text, and the first 300 characters of the response body for diagnostics. The request uses Bearer token auth with the GitHub API version 2022-11-28 header. Common causes are authentication failures (401/403), rate limiting (403 with rate limit headers), not-found (404 for wrong repo/tag), or server errors (5xx).

Source

Thrown at config/scripts/verify-release-required-assets.mjs:60

      names.add(new URL(value).pathname.split('/').findLast(Boolean) ?? value)
    } catch {
      names.add(value.split('/').findLast(Boolean) ?? value)
    }
  }
  return [...names]
}

async function githubFetch(url, token, accept = 'application/vnd.github+json') {
  const res = await fetch(url, {
    headers: {
      Accept: accept,
      Authorization: `Bearer ${token}`,
      'X-GitHub-Api-Version': API_VERSION
    }
  })
  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
}

async function fetchRelease(repo, tag, token) {
  // The publish gate runs while the release is still draft.
  const res = await githubFetch(`https://api.github.com/repos/${repo}/releases?per_page=100`, token)
  const releases = await res.json()
  if (!Array.isArray(releases)) {
    throw new Error(`GitHub releases response for ${repo} was not an array`)
  }
  const release = releases.find((candidate) => candidate.tag_name === tag)
  if (!release) {
    throw new Error(`Release ${repo}@${tag} was not found in the draft-aware releases list`)
  }
  return release
}

View on GitHub (pinned to 1136503c6a)

Solutions

  1. Read the status code and body from the error message: 401/403 means auth or permission issues, 404 means wrong repo/tag, 403 with 'rate limit' means throttling.
  2. Verify the token is valid and has not expired: echo $GH_TOKEN and test it with curl -H "Authorization: Bearer $GH_TOKEN" https://api.github.com/user.
  3. Ensure the token has repo scope for private repositories and draft release access.
  4. If rate-limited, wait for the reset window (check X-RateLimit-Reset header) or use a token with higher limits.
  5. For 404 errors, verify GITHUB_REPOSITORY is set correctly and the tag exists in the releases list.

Example fix

// before — no retry on transient failures
//   const res = await githubFetch(url, token)
//
// after — add exponential backoff retry for 5xx and rate-limit responses
//   async function githubFetchWithRetry(url, token, maxRetries = 3) {
//     for (let attempt = 0; attempt <= maxRetries; attempt++) {
//       const res = await fetch(url, { headers: { Authorization: `Bearer ${token}`, ... } })
//       if (res.ok) return res
//       if (res.status >= 500 || res.status === 429) {
//         if (attempt === maxRetries) throw new Error(`GitHub request failed after ${maxRetries} retries: ${res.status}`)
//         await new Promise(r => setTimeout(r, 2 ** attempt * 1000))
//         continue
//       }
//       throw new Error(`GitHub request failed ${res.status}: ${(await res.text()).slice(0, 300)}`)
//     }
//   }
Defensive patterns

Strategy: retry

Validate before calling

// Validate token and connectivity before making API calls.
async function preCheckGitHubAccess(token, repo) {
  const res = await fetch('https://api.github.com/user', {
    headers: { Authorization: `Bearer ${token}`, Accept: 'application/vnd.github+json' }
  })
  if (!res.ok) {
    return { ok: false, message: `Token invalid or expired (HTTP ${res.status})` }
  }
  const user = await res.json()
  return { ok: true, user: user.login }
}

Try / catch

// Retry on transient failures (5xx, rate limit); fail fast on auth/not-found.
async function githubFetchWithRetry(url, token, maxRetries = 3) {
  for (let attempt = 0; attempt <= maxRetries; attempt++) {
    const res = await fetch(url, {
      headers: {
        Accept: 'application/vnd.github+json',
        Authorization: `Bearer ${token}`,
        'X-GitHub-Api-Version': '2022-11-28'
      }
    })
    if (res.ok) return res
    const body = await res.text().catch(() => '')
    // Retry only on server errors and rate limits
    if (res.status >= 500 || res.status === 429) {
      if (attempt === maxRetries) {
        throw new Error(`GitHub request failed after ${maxRetries + 1} attempts: ${res.status} ${body.slice(0, 300)}`)
      }
      const retryAfter = parseInt(res.headers.get('Retry-After') || '2', 10)
      await new Promise((r) => setTimeout(r, retryAfter * 1000))
      continue
    }
    // Non-retryable: auth, not found, etc.
    throw new Error(`GitHub request failed ${res.status} ${res.statusText}: ${body.slice(0, 300)}`)
  }
}

Prevention

When it happens

Trigger: Any GitHub API call in the release verification flow (fetching releases list, fetching asset content) returns a non-2xx status. The token (from GH_TOKEN or GITHUB_TOKEN env var) may be expired, lack permissions for the repo, the repo name may be wrong, the rate limit may be exceeded, or GitHub may be experiencing an outage.

Common situations: An expired or revoked GitHub token in CI; insufficient token scopes (needs repo access for private repos or draft releases); GITHUB_REPOSITORY env var set to the wrong repo; GitHub API rate limit hit (60/hr unauthenticated, 5000/hr authenticated); the release tag doesn't exist yet (404); GitHub API is temporarily unavailable (5xx).

Related errors


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