anuraghazra/github-readme-stats · critical · CustomError

NO_TOKENS

NO_TOKENS

Error message

No GitHub API tokens found

What it means

Thrown by the retryer on its very first line when RETRIES is 0. RETRIES is computed once at module load as the count of environment variables matching /PAT_\d*$/ (or hardcoded to 7 when NODE_ENV === 'test'). With zero PAT_N variables there is no token to authenticate any GitHub API call, so the retryer refuses to run. The secondary message directs the user to add a PAT_1 env var.

Source

Thrown at src/common/retryer.js:29

).length;
const RETRIES = process.env.NODE_ENV === "test" ? 7 : PATs;

/**
 * @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,
    );

View on GitHub (pinned to 54a7985aee)

Solutions

  1. Create a fine-grained or classic GitHub personal access token with public_repo (and read:user) scopes.
  2. Set PAT_1 in the deployment environment (Vercel Project Settings > Environment Variables, or `export PAT_1=...` locally / in .env).
  3. Verify with `printenv | grep '^PAT_'` that the variable is actually visible to the Node process, and that NODE_ENV is not 'test' in production (which would mask a missing-token misconfiguration).
  4. For higher traffic, add PAT_2, PAT_3, ... to increase the retry budget.

Example fix

// before: no token env vars set

// after (Vercel CLI)
// vercel env add PAT_1
// or locally:
// echo "PAT_1=ghp_xxxxxxxxxxxxxxxxxxxx" >> .env
Defensive patterns

Strategy: validation

Validate before calling

// Fail fast at boot if no tokens are configured.
function assertTokensConfigured() {
  const count = Object.keys(process.env).filter((k) => /PAT_\d*$/.test(k)).length;
  if (count === 0 && process.env.NODE_ENV !== "test") {
    throw new Error(
      "No PAT_N env vars set. Add PAT_1 with a GitHub token before starting the server.",
    );
  }
}
// call during module init / before listen()

Try / catch

// In a server bootstrap, surface a clear startup error instead of failing per-request.
try {
  assertTokensConfigured();
} catch (e) {
  console.error(e.message);
  process.exit(1);
}

Prevention

When it happens

Trigger: Any request to any endpoint (stats, pin, gist, top-langs, wakatime) when the deployed environment defines no PAT_1, PAT_2, ... variables. Concretely: Object.keys(process.env).filter(key => /PAT_\d*$/.exec(key)).length === 0 and NODE_ENV !== 'test'.

Common situations: A fresh Vercel/local clone where the PAT_1 environment variable was never set; the variable was renamed (e.g. set as GITHUB_TOKEN instead of PAT_1); the variable is scoped to a different Vercel environment (e.g. set only in 'Preview' but hit on 'Production'); or a .env file is present but dotenv wasn't loaded before this module initialized.

Related errors


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