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

  1. 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.
  2. 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.
  3. 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.
  4. Reduce the polling interval (setInterval(fetchStats, 60000)) or cache stats server-side to stay under the rate limit.
  5. For 5xx statuses, simply retry later or add exponential backoff; the error is transient on GitHub's side.
  6. 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

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.