{"record":{"id":"67df03961d3c2152","repo":"stablyai/orca","slug":"github-releases-response-for-repo-was-not-an-ar-67df03","errorCode":null,"errorMessage":"GitHub releases response for ${repo} was not an array","messagePattern":"GitHub releases response for (.+?) was not an array","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"config/scripts/verify-release-required-assets.mjs","lineNumber":70,"sourceCode":"    headers: {\n      Accept: accept,\n      Authorization: `Bearer ${token}`,\n      'X-GitHub-Api-Version': API_VERSION\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\n}\n\nasync function fetchRelease(repo, tag, token) {\n  // The publish gate runs while the release is still draft.\n  const res = await githubFetch(`https://api.github.com/repos/${repo}/releases?per_page=100`, token)\n  const releases = await res.json()\n  if (!Array.isArray(releases)) {\n    throw new Error(`GitHub releases response for ${repo} was not an array`)\n  }\n  const release = releases.find((candidate) => candidate.tag_name === tag)\n  if (!release) {\n    throw new Error(`Release ${repo}@${tag} was not found in the draft-aware releases list`)\n  }\n  return release\n}\n\nasync function fetchAssetText(repo, asset, token) {\n  const res = await githubFetch(\n    `https://api.github.com/repos/${repo}/releases/assets/${asset.id}`,\n    token,\n    'application/octet-stream'\n  )\n  return res.text()\n}\n\nexport async function verifyRequiredReleaseAssets({ repo, tag, token }) {","sourceCodeStart":52,"sourceCodeEnd":88,"githubUrl":"https://github.com/stablyai/orca/blob/1136503c6a231a16dce8f921f6fadb63d181e8db/config/scripts/verify-release-required-assets.mjs#L52-L88","documentation":"Thrown by the release asset verification script's fetchRelease function when the JSON body of the GitHub releases list endpoint is not a JavaScript array. The releases API endpoint (GET /repos/{repo}/releases) normally returns an array of release objects, so a non-array response indicates an unexpected API response shape — typically an error object (when auth failed but somehow passed the res.ok check, or a redirect/HTML response), or a GitHub API versioning change.","triggerScenarios":"fetchRelease calls githubFetch which returns res.ok=true (2xx), then res.json() produces a non-array. This can occur if GitHub returns an object with an error property instead of an array (some edge cases with deprecated API versions), if the response was an HTML redirect page that happened to be 200, or if a proxy/interceptor altered the response.","commonSituations":"The X-GitHub-Api-Version header points to a deprecated or preview API version that returns a different response shape; a corporate proxy or man-in-the-middle intercept returns HTML instead of JSON; GitHub returns a 200 with an error object for certain edge cases (e.g., repo archived); the API_VERSION constant (2022-11-28) becomes outdated after a GitHub API deprecation.","solutions":["Log the actual response body to see what shape was returned: add console.error(await res.text()) before the json() call in a debug build.","Verify the X-GitHub-Api-Version header value (2022-11-28) is still supported by checking GitHub's API changelog.","Check for proxy interference: if behind a corporate proxy, verify it passes through GitHub API responses unmodified.","If the response is an error object (e.g., { message: '...' }), the preceding githubFetch check should have caught it — investigate why res.ok was true for an error response.","Add a more specific type check that includes a helpful diagnostic: if (!Array.isArray(releases)) throw new Error(`Expected array, got ${typeof releases}: ${JSON.stringify(releases).slice(0, 200)}`)."],"exampleFix":"// before\n//   const releases = await res.json()\n//   if (!Array.isArray(releases)) {\n//     throw new Error(`GitHub releases response for ${repo} was not an array`)\n//   }\n//\n// after — include the actual response for diagnostics\n//   const body = await res.json()\n//   if (!Array.isArray(body)) {\n//     throw new Error(`GitHub releases response for ${repo} was not an array (got ${typeof body}): ${JSON.stringify(body).slice(0, 200)}`)\n//   }\n//   const releases = body","handlingStrategy":"type-guard","validationCode":null,"typeGuard":"// Narrow that a GitHub releases response is the expected array shape.\nfunction isReleasesArray(value) {\n  return Array.isArray(value) && value.every(\n    (item) => typeof item === 'object' && item !== null && typeof item.tag_name === 'string'\n  )\n}","tryCatchPattern":"// Parse the body once, validate the shape, and include diagnostics on failure.\nconst body = await res.json()\nif (!isReleasesArray(body)) {\n  const bodyType = Array.isArray(body) ? `array of ${body.length} non-release items` : typeof body\n  throw new Error(\n    `GitHub releases response for ${repo} was not a valid releases array (got ${bodyType}): ${JSON.stringify(body).slice(0, 200)}`\n  )\n}\nconst releases = body","preventionTips":["Log the raw response body and content-type header when the shape is unexpected, to diagnose proxy or API version issues.","Pin the X-GitHub-Api-Version header to a stable version and monitor GitHub's API deprecation announcements.","If behind a corporate proxy, verify it passes through GitHub API JSON responses without modification."],"tags":["github-api","response-validation","release-verification","ci-gate","type-safety"],"backgroundTag":null,"analyzedSha":"1136503c6a231a16dce8f921f6fadb63d181e8db","analyzedAt":"2026-08-12T23:15:58.167Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}