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) {
breakView on GitHub (pinned to 1136503c6a)
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.
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
- 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.
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
- GitHub releases response page ${page} for ${repo} was not an
- repo is required
- token is required
- GitHub request failed ${res.status} ${res.statusText}: ${bod
- GitHub request failed ${res.status} ${res.statusText}: ${bod
AI-assisted analysis of stablyai/orca@1136503c6a (2026-08-12).
Data as JSON: /api/errors/9e0c914f4eecc9c1.
Report an issue: GitHub.