jamiepine/voicebox · warning · Error

releases ${r.status}

Error message

releases ${r.status}

What it means

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.

Source

Thrown at landing/src/app/download/page.tsx:85

  useEffect(() => {
    const fromQuery = parseQueryPlatform(window.location.search);
    const resolved = fromQuery ?? detectPlatform();
    // No prebuilt Linux binary yet — send Linux users to the build-from-source
    // instructions instead of sitting on /download trying to trigger a
    // download that doesn't exist.
    if (resolved === 'linux') {
      window.location.replace('/linux-install');
      return;
    }
    setPlatform(resolved);
  }, []);

  useEffect(() => {
    let cancelled = false;
    fetch('/api/releases')
      .then((r) => {
        if (!r.ok) throw new Error(`releases ${r.status}`);
        return r.json();
      })
      .then((data) => {
        if (cancelled) return;
        if (data.downloadLinks) setLinks(data.downloadLinks as DownloadLinks);
      })
      .catch(() => {
        if (!cancelled) setLinksError(true);
      });
    return () => {
      cancelled = true;
    };
  }, []);

  useEffect(() => {
    if (triggered || !links || !platform) return;
    const url = links[platform];
    if (!url) return;

View on GitHub (pinned to 51f49dea19)

Solutions

  1. Inspect /api/releases server logs for the upstream GitHub status.
  2. Ensure the download page's linksError UI provides working fallback links (the constructed baseUrl URLs in releases.ts).
  3. Add a GitHub token server-side to avoid the 60/hr unauthenticated cap.
  4. Verify GITHUB_REPO is correct and the latest release is published.

Example fix

// before
if (!r.ok) throw new Error(`releases ${r.status}`);
// after
if (!r.ok) { if (!cancelled) setLinksError(true); return; }
Defensive patterns

Strategy: fallback

Type guard

function isDownloadLinks(v: unknown): v is DownloadLinks {
  return typeof v === 'object' && v !== null
    && typeof (v as DownloadLinks).macArm === 'string'
    && typeof (v as DownloadLinks).windows === 'string';
}

Try / catch

try {
  const r = await fetch('/api/releases');
  if (!r.ok) throw new Error(`releases ${r.status}`);
  const data = await r.json();
  if (!cancelled && isDownloadLinks(data.downloadLinks)) setLinks(data.downloadLinks);
} catch {
  if (!cancelled) setLinksError(true);
}

Prevention

When it happens

Trigger: 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.

Common situations: 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).

Related errors


AI-assisted analysis of jamiepine/voicebox@51f49dea19 (2026-08-12). Data as JSON: /api/errors/d9573f144b9d2115. Report an issue: GitHub.