rahuldkjain/github-profile-readme-generator · warning · Error
Failed to fetch stats (${response.status})
Error message
Failed to fetch stats (${response.status}) What it means
fetchStats throws this generic Error whenever the GitHub REST API returns a non-ok HTTP status that is not the handled rate-limit case (403 with X-RateLimit-Remaining: 0). The component calls the unauthenticated endpoint https://api.github.com/repos/rahuldkjain/github-profile-readme-generator to display star/fork counts, and any unexpected status (404, 5xx, 403 without rate-limit header, etc.) becomes this error. It is caught by the component itself, which sets an error state, hides the widget, and shows a toast on first load.
Source
Thrown at src/components/ui/github-stats.tsx:45
const fetchStats = async () => {
if (!shouldRequestStats()) return;
try {
const response = await fetch(
'https://api.github.com/repos/rahuldkjain/github-profile-readme-generator'
);
if (!response.ok) {
if (response.status === 403) {
const rateLimitRemaining = response.headers.get('X-RateLimit-Remaining');
if (rateLimitRemaining === '0') {
console.warn('GitHub API rate limit exceeded for stats');
setError(true);
setIsLoading(false);
return;
}
}
throw new Error(`Failed to fetch stats (${response.status})`);
}
const data = await response.json();
setStats({
stars: data.stargazers_count || 0,
forks: data.forks_count || 0,
});
setIsLoading(false);
} catch (err) {
console.error('Error fetching GitHub stats:', err);
setError(true);
setIsLoading(false);
// Only show error toast on first load, not on periodic refreshes
if (stats === null) {
errorToast(
'Failed to load GitHub stats',
"Unable to fetch repository statistics. This won't affect the generator functionality.",View on GitHub (pinned to 5ae90bfcbd)
Solutions
- Open the browser network tab and read the actual status code in the message (e.g. 404 vs 503) to identify the real cause before changing code.
- If status is 404, verify the repo path 'rahuldkjain/github-profile-readme-generator' is correct, public, and not renamed; update the hardcoded URL if you forked/renamed it.
- If status is 403/429, check the X-RateLimit-Remaining and X-RateLimit-Reset headers; unauthenticated GitHub API allows only ~60 requests/hour per IP, and this component polls every 60 seconds.
- Reduce the polling interval (setInterval(fetchStats, 60000)) or cache stats server-side to stay under the rate limit.
- For 5xx statuses, simply retry later or add exponential backoff; the error is transient on GitHub's side.
- Check for proxies/ad-blockers/extensions intercepting api.github.com and stripping headers or blocking the request.
Example fix
// before
throw new Error(`Failed to fetch stats (${response.status})`);
// after
if (response.status === 404) {
console.error('Repo not found: check the repository slug in the fetch URL');
}
throw new Error(`Failed to fetch stats (${response.status})`); Defensive patterns
Strategy: try-catch
Validate before calling
const res = await fetch('https://api.github.com/repos/rahuldkjain/github-profile-readme-generator', { method: 'HEAD' });
if (!res.ok) console.warn(`GitHub API unavailable (status ${res.status}); skipping stats fetch`); Type guard
function isGitHubStats(data: unknown): data is { stargazers_count: number; forks_count: number } {
return typeof data === 'object' && data !== null &&
'stargazers_count' in data && typeof (data as any).stargazers_count === 'number' &&
'forks_count' in data && typeof (data as any).forks_count === 'number';
} Try / catch
try {
const response = await fetch('https://api.github.com/repos/owner/repo');
if (!response.ok) {
if (response.status === 403 && response.headers.get('X-RateLimit-Remaining') === '0') {
return null; // treat rate limit as silent fallback
}
throw new Error(`Failed to fetch stats (${response.status})`);
}
const data = await response.json();
return { stars: data.stargazers_count, forks: data.forks_count };
} catch (err) {
console.warn('GitHub stats unavailable, using fallback:', err);
return null; // render widget with cached/default values instead of erroring
} Prevention
- Cache GitHub stats server-side or in localStorage instead of polling the API every 60 seconds from the client.
- Include a GitHub token (via a server-side proxy, never in client code) to raise the rate limit from 60 to 5000 requests/hour.
- Always check response.ok before calling response.json() on any fetch.
- Handle 403 with the X-RateLimit-Remaining header explicitly, as this component does, and back off using X-RateLimit-Reset.
- Render the widget defensively (return null or cached values on failure) so a stats failure never breaks the page.
When it happens
Trigger: GitHub returns any non-2xx status other than rate-limited 403: e.g. 404 if the repo slug is wrong or renamed, 502/503 when GitHub API is degraded, 403 blocked by a proxy/CDN or for abuse without X-RateLimit-Remaining: 0, or 451 for region-blocked content.
Common situations: Developers hit this when the repository was renamed/made private (404), when GitHub has a partial outage (5xx), when a corporate proxy or ad-blocker rewrites api.github.com responses, or when the unauthenticated 60 req/hour rate limit is hit but the X-RateLimit-Remaining header is stripped by a proxy so the special 403 branch is missed. It also fires in local dev behind firewalls blocking api.github.com.
AI-assisted analysis of rahuldkjain/github-profile-readme-generator@5ae90bfcbd (2026-08-31).
Data as JSON: /api/errors/d2f30dd903accd57.
Report an issue: GitHub.