anuraghazra/github-readme-stats · error · CustomError

MAX_RETRY

MAX_RETRY

Error message

Downtime due to GitHub API rate limiting

What it means

Thrown by the retryer once the recursion counter exceeds RETRIES (the number of available PAT_N tokens). The retryer increments 'retries' and recurses whenever GitHub signals RATE_LIMITED (by error type or a /rate limit/i message), returns 'Bad credentials', or returns 'account suspended'. When every token has been tried and exhausted, retries > RETRIES and this MAX_RETRY error fires. The secondary message tells users they can self-host or wait.

Source

Thrown at src/common/retryer.js:33

 * @typedef {import("axios").AxiosResponse} AxiosResponse Axios response.
 * @typedef {(variables: any, token: string, retriesForTests?: number) => Promise<AxiosResponse>} FetcherFunction Fetcher function.
 */

/**
 * Try to execute the fetcher function until it succeeds or the max number of retries is reached.
 *
 * @param {FetcherFunction} fetcher The fetcher function.
 * @param {any} variables Object with arguments to pass to the fetcher function.
 * @param {number} retries How many times to retry.
 * @returns {Promise<any>} The response from the fetcher function.
 */
const retryer = async (fetcher, variables, retries = 0) => {
  if (!RETRIES) {
    throw new CustomError("No GitHub API tokens found", CustomError.NO_TOKENS);
  }

  if (retries > RETRIES) {
    throw new CustomError(
      "Downtime due to GitHub API rate limiting",
      CustomError.MAX_RETRY,
    );
  }

  try {
    // try to fetch with the first token since RETRIES is 0 index i'm adding +1
    let response = await fetcher(
      variables,
      // @ts-ignore
      process.env[`PAT_${retries + 1}`],
      // used in tests for faking rate limit
      retries,
    );

    // react on both type and message-based rate-limit signals.
    // https://github.com/anuraghazra/github-readme-stats/issues/4425
    const errors = response?.data?.errors;

View on GitHub (pinned to 54a7985aee)

Solutions

  1. Add more tokens (PAT_2, PAT_3, ...) so the retryer has more attempts before giving up.
  2. Wait for the rate-limit window to reset (GitHub resets hourly) and retry.
  3. Self-host the instance so you control token allocation, as the secondary message suggests.
  4. Audit existing tokens for revocation/suspension — a single dead token is fine, but if all are dead every request walks the full list and fails.

Example fix

// before: single exhausted/revoked token
// PAT_1=ghp_revoked_or_rate_limited

// after: pool of valid tokens
// PAT_1=ghp_xxx
// PAT_2=ghp_yyy
// PAT_3=ghp_zzz
Defensive patterns

Strategy: retry

Validate before calling

// Pre-flight: estimate remaining GitHub rate budget before issuing the request.
// (Informational only — reduces, but cannot guarantee, MAX_RETRY.)
async function remainingRateBudget(token) {
  const r = await fetch("https://api.github.com/rate_limit", {
    headers: { Authorization: `token ${token}` },
  });
  const j = await r.json();
  return j.resources.graphql.remaining;
}

Try / catch

// Distinguish MAX_RETRY from other errors so you can back off rather than fail.
import { CustomError } from "../src/common/error.js";
try {
  const stats = await fetchStats(username);
} catch (err) {
  if (err instanceof CustomError && err.type === CustomError.MAX_RETRY) {
    // schedule a retry after the GitHub rate-limit window resets
    await new Promise((r) => setTimeout(r, 60_000));
    return fetchStats(username);
  }
  throw err;
}

Prevention

When it happens

Trigger: All configured PAT_N tokens are rate-limited within one request: the retryer calls fetcher with PAT_1, sees RATE_LIMITED, recurses with PAT_2, and so on until retries > RETRIES. Also fires when every token returns 'Bad credentials' (revoked) or 'Sorry. Your account was suspended.', because those same branches recurse and burn the retry budget.

Common situations: Public Vercel instance under load where the shared token pool is exhausted; a token revoked by GitHub so each retry hits 'Bad credentials' and walks the whole list; suspended accounts; or a traffic spike against a self-hosted instance with too few tokens.

Related errors


AI-assisted analysis of anuraghazra/github-readme-stats@54a7985aee (2026-08-12). Data as JSON: /api/errors/7f814a359bac7803. Report an issue: GitHub.