{"record":{"id":"d9573f144b9d2115","repo":"jamiepine/voicebox","slug":"releases-r-status","errorCode":null,"errorMessage":"releases ${r.status}","messagePattern":"releases (.+?)","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"warning","filePath":"landing/src/app/download/page.tsx","lineNumber":85,"sourceCode":"\n  useEffect(() => {\n    const fromQuery = parseQueryPlatform(window.location.search);\n    const resolved = fromQuery ?? detectPlatform();\n    // No prebuilt Linux binary yet — send Linux users to the build-from-source\n    // instructions instead of sitting on /download trying to trigger a\n    // download that doesn't exist.\n    if (resolved === 'linux') {\n      window.location.replace('/linux-install');\n      return;\n    }\n    setPlatform(resolved);\n  }, []);\n\n  useEffect(() => {\n    let cancelled = false;\n    fetch('/api/releases')\n      .then((r) => {\n        if (!r.ok) throw new Error(`releases ${r.status}`);\n        return r.json();\n      })\n      .then((data) => {\n        if (cancelled) return;\n        if (data.downloadLinks) setLinks(data.downloadLinks as DownloadLinks);\n      })\n      .catch(() => {\n        if (!cancelled) setLinksError(true);\n      });\n    return () => {\n      cancelled = true;\n    };\n  }, []);\n\n  useEffect(() => {\n    if (triggered || !links || !platform) return;\n    const url = links[platform];\n    if (!url) return;","sourceCodeStart":67,"sourceCodeEnd":103,"githubUrl":"https://github.com/jamiepine/voicebox/blob/51f49dea198384b4eb6087b72c17057c6eb1c1cd/landing/src/app/download/page.tsx#L67-L103","documentation":"Thrown in the /download page useEffect when /api/releases returns non-2xx. Unlike the home/capture pages, the failure here sets `linksError` state to render a degraded download UI rather than just logging. The route is the same Next.js proxy to GitHub's releases API.","triggerScenarios":"GET /api/releases returns 500 (GitHub upstream failure in getLatestRelease()) or is unreachable. The component then sets linksError=true to show fallback/legacy download links.","commonSituations":"GitHub rate limit hit on the deploy IP. GITHUB_REPO misconfigured. A release has no matching assets so downloadLinks are incomplete (note: that yields ok with missing fields, not this error).","solutions":["Inspect /api/releases server logs for the upstream GitHub status.","Ensure the download page's linksError UI provides working fallback links (the constructed baseUrl URLs in releases.ts).","Add a GitHub token server-side to avoid the 60/hr unauthenticated cap.","Verify GITHUB_REPO is correct and the latest release is published."],"exampleFix":"// before\nif (!r.ok) throw new Error(`releases ${r.status}`);\n// after\nif (!r.ok) { if (!cancelled) setLinksError(true); return; }","handlingStrategy":"fallback","validationCode":null,"typeGuard":"function isDownloadLinks(v: unknown): v is DownloadLinks {\n  return typeof v === 'object' && v !== null\n    && typeof (v as DownloadLinks).macArm === 'string'\n    && typeof (v as DownloadLinks).windows === 'string';\n}","tryCatchPattern":"try {\n  const r = await fetch('/api/releases');\n  if (!r.ok) throw new Error(`releases ${r.status}`);\n  const data = await r.json();\n  if (!cancelled && isDownloadLinks(data.downloadLinks)) setLinks(data.downloadLinks);\n} catch {\n  if (!cancelled) setLinksError(true);\n}","preventionTips":["Render fallback download URLs when linksError is true (baseUrl constructions in releases.ts).","Add a server-side GitHub token to reduce rate-limit failures.","Verify the latest release has matching assets for all platforms."],"tags":["network","external-api","github","nextjs","releases","landing","download"],"backgroundTag":null,"analyzedSha":"51f49dea198384b4eb6087b72c17057c6eb1c1cd","analyzedAt":"2026-08-12T16:51:42.824Z","schemaVersion":2},"datasetVersion":"2026-08-12T18:17:37.767Z"}