jamiepine/voicebox · info · Error
Failed to fetch releases
Error message
Failed to fetch releases
What it means
Thrown inside a useEffect on the /capture page when the internal /api/releases route returns non-2xx. The promise chain catches it and only console.errors, so the page still renders — version/totalDownloads stay null. /api/releases proxies GitHub's releases API via getLatestRelease() and returns 500 if that upstream fetch fails.
Source
Thrown at landing/src/app/capture/page.tsx:20
import { Github } from 'lucide-react';
import { useEffect, useState } from 'react';
import { AgentIntegration } from '@/components/AgentIntegration';
import { CaptureHero } from '@/components/CaptureHero';
import { CapturesMockup } from '@/components/CapturesMockup';
import { Footer } from '@/components/Footer';
import { Navbar } from '@/components/Navbar';
import { AppleIcon, LinuxIcon, WindowsIcon } from '@/components/PlatformIcons';
import { GITHUB_REPO } from '@/lib/constants';
export default function CapturePage() {
const [version, setVersion] = useState<string | null>(null);
const [totalDownloads, setTotalDownloads] = useState<number | null>(null);
useEffect(() => {
fetch('/api/releases')
.then((res) => {
if (!res.ok) throw new Error('Failed to fetch releases');
return res.json();
})
.then((data) => {
if (data.version) setVersion(data.version);
if (data.totalDownloads != null) setTotalDownloads(data.totalDownloads);
})
.catch((error) => {
console.error('Failed to fetch release info:', error);
});
}, []);
return (
<>
<Navbar />
{/* ── Hero ─────────────────────────────────────────────────── */}
<CaptureHero version={version} totalDownloads={totalDownloads} />
View on GitHub (pinned to 51f49dea19)
Solutions
- Check the server logs for getLatestRelease()'s underlying GitHub status (the /api/releases route logs 'Error fetching release info').
- If rate-limited, configure a GitHub token for the server-side fetch to raise the limit.
- Treat null version gracefully in the UI (hide the version badge instead of showing an error).
- Confirm GITHUB_REPO in landing/src/lib/releases.ts is correct and public.
Example fix
// before
if (!res.ok) throw new Error('Failed to fetch releases');
// after
if (!res.ok) { console.warn('releases status', res.status); return; } Defensive patterns
Strategy: fallback
Type guard
interface ReleasePayload { version?: string; totalDownloads?: number; error?: string }
function isReleasePayload(v: unknown): v is ReleasePayload {
return typeof v === 'object' && v !== null && (!('error' in v));
} Try / catch
try {
const res = await fetch('/api/releases');
if (!res.ok) throw new Error('Failed to fetch releases');
const data = await res.json();
if (data.version) setVersion(data.version);
} catch (error) {
console.error('Failed to fetch release info:', error);
// version stays null; UI hides the badge
} Prevention
- Treat null version as 'hide the badge' rather than an error state.
- Configure a GitHub token server-side to avoid the 60/hr unauthenticated cap.
- Confirm GITHUB_REPO is correct and public.
When it happens
Trigger: GET /api/releases responds 500 because getLatestRelease() threw (GitHub API rate-limited at 403/429, repo not found, network). The deployed Next.js instance's egress to api.github.com is blocked.
Common situations: GitHub unauthenticated rate limit (60 req/hr per IP) exhausted by a shared deploy or preview builds. GITHUB_REPO constant points at a non-existent or private repo. CI/preview environment with no outbound network.
Related errors
- releases ${r.status}
- Failed to fetch releases
- Failed to fetch stars
- Jupiter HTTP ${res.status}
- pump.fun swap-api HTTP ${res.status}
AI-assisted analysis of jamiepine/voicebox@51f49dea19 (2026-08-12).
Data as JSON: /api/errors/85cdb6946debb932.
Report an issue: GitHub.