{"record":{"id":"f1b420d249380f21","repo":"schollz/croc","slug":"github-returned-invalid-release-metadata","errorCode":null,"errorMessage":"GitHub returned invalid release metadata","messagePattern":"GitHub returned invalid release metadata","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"web/src/releases.ts","lineNumber":138,"sourceCode":"    assets[0]\n  );\n}\n\nexport async function fetchLatestRelease(signal?: AbortSignal) {\n  const response = await fetch(latestReleaseAPI, {\n    signal,\n    headers: { Accept: \"application/vnd.github+json\" },\n  });\n  if (!response.ok) {\n    throw new Error(`GitHub release request failed (${response.status})`);\n  }\n  const release = (await response.json()) as GitHubRelease;\n  if (\n    !release.tag_name ||\n    !release.html_url ||\n    !Array.isArray(release.assets)\n  ) {\n    throw new Error(\"GitHub returned invalid release metadata\");\n  }\n  return release;\n}\n","sourceCodeStart":120,"sourceCodeEnd":142,"githubUrl":"https://github.com/schollz/croc/blob/e25f1bdc04f07f094d50b0a1bf67e2563944b57a/web/src/releases.ts#L120-L142","documentation":"Thrown by fetchLatestRelease() when GitHub returns 200 but the JSON body is not release-shaped: tag_name or html_url missing/falsy, or assets not an array. It protects the downstream asset-selection code (assetArchitecture / assets.find) from an unexpected payload shape.","triggerScenarios":"A 200 response lacking tag_name/html_url/assets: a proxy returning an error page with 200, a test fetch mock returning {}, or a GitHub API schema change after deprecation.","commonSituations":"Test fixtures stubbing fetch with minimal objects; intercepting proxies rewriting responses; future GitHub API versions dropping fields.","solutions":["In tests, mock fetch with { tag_name, html_url, assets: [...] }","Bypass or fix intermediaries that rewrite api.github.com responses","Track the GitHub REST API changelog for release schema changes"],"exampleFix":"// before (test stub that triggers the error)\nfetchMock.mockResolvedValue(new Response(JSON.stringify({}), { status: 200 }));\n\n// after\nfetchMock.mockResolvedValue(new Response(JSON.stringify({\n  tag_name: 'v1.0.0',\n  html_url: 'https://github.com/o/r/releases/v1.0.0',\n  assets: [],\n}), { status: 200 }));","handlingStrategy":"type-guard","validationCode":"const body = await response.json();\nif (!isGitHubRelease(body)) throw new Error('unexpected release payload shape');","typeGuard":"function isGitHubRelease(v: unknown): v is GitHubRelease {\n  const r = v as Record<string, unknown>;\n  return typeof r?.tag_name === 'string' && r.tag_name !== '' &&\n    typeof r?.html_url === 'string' && Array.isArray(r?.assets);\n}","tryCatchPattern":"try { return await fetchLatestRelease(signal); } catch (e) { if (e instanceof Error && e.message === 'GitHub returned invalid release metadata') invalidateReleaseCache(); throw e; }","preventionTips":["Stub fetch in tests with fixtures containing tag_name, html_url, assets","Watch the GitHub REST changelog for release schema changes"],"tags":["github-api","validation","schema","testing"],"backgroundTag":null,"analyzedSha":"e25f1bdc04f07f094d50b0a1bf67e2563944b57a","analyzedAt":"2026-08-15T12:53:39.096Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}