janhq/jan · warning · Error
Failed to fetch releases
Error message
Failed to fetch releases
What it means
Thrown when `fetch('https://api.github.com/repos/janhq/jan/releases')` returns a non-OK response. GitHub's unauthenticated API is rate-limited to 60 requests/hour per IP, and 403 is the most common non-OK status. No auth token is attached, so this is purely the anonymous endpoint.
Source
Thrown at web-app/src/hooks/useReleaseNotes.ts:30
type ReleaseState = {
release: Release | null
loading: boolean
error: string | null
fetchLatestRelease: (includeBeta: boolean) => Promise<void>
}
export const useReleaseNotes = create<ReleaseState>((set) => ({
release: null,
loading: false,
error: null,
fetchLatestRelease: async (includeBeta: boolean) => {
set({ loading: true, error: null })
try {
const res = await fetch(
'https://api.github.com/repos/janhq/jan/releases'
)
if (!res.ok) throw new Error('Failed to fetch releases')
const releases = await res.json()
const stableRelease = releases.find(
(release: { prerelease: boolean; draft: boolean }) =>
!release.prerelease && !release.draft
)
const betaRelease = releases.find(
(release: { prerelease: boolean }) => release.prerelease
)
const selected = includeBeta
? (betaRelease ?? stableRelease)
: stableRelease
set({ release: selected, loading: false })
} catch (err: any) {
set({ error: err.message, loading: false })
}
},View on GitHub (pinned to fad3f12a14)
Solutions
- Add a conditional-request (ETag/Last-Modified) or cache the result to reduce calls below the rate limit.
- Attach a GitHub token (even a public-read token raises the limit to 5000/hr).
- Fall back to the releases HTML page or a CDN-hosted version file.
- Handle 403 specifically with a 'rate-limited, try later' message instead of a generic failure.
Example fix
// before
const res = await fetch('https://api.github.com/repos/janhq/jan/releases')
if (!res.ok) throw new Error('Failed to fetch releases')
// after
const headers: Record<string, string> = {}
if (import.meta.env.VITE_GITHUB_TOKEN) {
headers.Authorization = `Bearer ${import.meta.env.VITE_GITHUB_TOKEN}`
}
const res = await fetch('https://api.github.com/repos/janhq/jan/releases', { headers })
if (!res.ok) {
throw new Error(
res.status === 403
? 'GitHub API rate limit reached. Try again later.'
: `Failed to fetch releases (HTTP ${res.status})`
)
} Defensive patterns
Strategy: retry
Validate before calling
// Cache the last successful fetch timestamp to avoid hammering the API.
const last = localStorage.getItem('lastReleaseFetch')
if (last && Date.now() - Number(last) < 60 * 60 * 1000) return // within an hour Type guard
function isRateLimited(res: Response): boolean {
return res.status === 403 || res.status === 429
} Try / catch
try {
const res = await fetch('https://api.github.com/repos/janhq/jan/releases', {
headers: token ? { Authorization: `Bearer ${token}` } : {},
})
if (!res.ok) {
if (isRateLimited(res)) {
set({ error: 'GitHub rate limit reached. Try again later.', loading: false })
return
}
throw new Error(`Failed to fetch releases (HTTP ${res.status})`)
}
// proceed
} catch (err: any) {
set({ error: err.message, loading: false })
} Prevention
- Cache release data and use conditional requests (ETag/If-None-Match).
- Attach a GitHub token to raise the rate limit from 60 to 5000/hr.
- Handle 403/429 specifically with a friendly rate-limit message.
- Fall back to a CDN-hosted version manifest.
When it happens
Trigger: Anonymous GitHub API rate limit exceeded (60/hr/IP) — returns 403 with a rate-limit body; the repo was renamed/moved (301/404); GitHub API outage (5xx); network/proxy blocking api.github.com.
Common situations: Shared NAT/office IP where many users exhaust the 60/hr budget; corporate proxy; GitHub incident; development machines hitting the endpoint on every app launch.
Related errors
- Failed to fetch supported backends: ${error instanceof Error
- API key rotation exhausted
- Failed to fetch models from ${provider.provider}: ${lastStat
- Checksum mismatch for ${name}; the download was corrupt or t
- API request failed with status ${response.status}: ${JSON.st
AI-assisted analysis of janhq/jan@fad3f12a14 (2026-08-12).
Data as JSON: /api/errors/33e0da01681c5501.
Report an issue: GitHub.