anuraghazra/github-readme-stats · error · CustomError
res.statusText
res.statusText
Error message
${res.data.errors[0].message} What it means
CustomError thrown by fetchStats when the GraphQL response has errors but the first error's type is not NOT_FOUND and it has a message. The thrown message is the first error message wrapped via wrapTextMultiline(...,90,1)[0] (line-wrapped to 90 cols), and notably the error's .type is set to res.statusText — the HTTP status text (e.g. 'OK', 'Forbidden') — not a stable CustomError constant. The secondaryMessage then becomes statusText too (via the SECONDARY_ERROR_MESSAGES fallback), so this is essentially a generic passthrough of any non-NOT_FOUND GraphQL error.
Source
Thrown at src/fetchers/stats.js:273
let res = await statsFetcher({
username,
includeMergedPullRequests: include_merged_pull_requests,
includeDiscussions: include_discussions,
includeDiscussionsAnswers: include_discussions_answers,
startTime: commits_year ? `${commits_year}-01-01T00:00:00Z` : undefined,
});
// Catch GraphQL errors.
if (res.data.errors) {
logger.error(res.data.errors);
if (res.data.errors[0].type === "NOT_FOUND") {
throw new CustomError(
res.data.errors[0].message || "Could not fetch user.",
CustomError.USER_NOT_FOUND,
);
}
if (res.data.errors[0].message) {
throw new CustomError(
wrapTextMultiline(res.data.errors[0].message, 90, 1)[0],
res.statusText,
);
}
throw new CustomError(
"Something went wrong while trying to retrieve the stats data using the GraphQL API.",
CustomError.GRAPHQL_ERROR,
);
}
const user = res.data.data.user;
stats.name = user.name || user.login;
// if include_all_commits, fetch all commits using the REST API.
if (include_all_commits) {
stats.totalCommits = await totalCommitsFetcher(username);
} else {View on GitHub (pinned to 54a7985aee)
Solutions
- Inspect the raw GraphQL error message (it is the thrown message) for the real cause.
- If permission-related, regenerate PAT_1 with read:user and public_repo scopes.
- Retry for transient GitHub-side GraphQL errors.
- If a field was deprecated, update the project or pin a compatible version.
Example fix
// before: token missing scopes // PAT_1 has no read:user scope -> GraphQL FORBIDDEN, message surfaced here // after: regenerate token with read:user + public_repo scopes
Defensive patterns
Strategy: try-catch
Validate before calling
// Loose pre-check that the token can read user data (reduces FORBIDDEN reaching this branch).
async function tokenCanReadUsers(token) {
const r = await fetch("https://api.github.com/user", {
headers: { Authorization: `token ${token}` },
});
return r.ok;
} Try / catch
// err.type here is res.statusText (e.g. 'Forbidden'), not a stable constant — log the message.
try {
return await fetchStats(username);
} catch (err) {
console.warn("stats GraphQL error:", err.message, "type/statusText:", err.type);
throw err;
} Prevention
- Don't switch on err.type for this error — it is res.statusText, not a CustomError constant. Switch on message content instead.
- Keep PAT_1 scopes at read:user + public_repo to avoid permission-driven GraphQL errors.
When it happens
Trigger: GitHub returns a GraphQL errors array with a message but a type other than NOT_FOUND — e.g. FORBIDDEN (token lacks scope), RATE_LIMITED that survived the retryer, or a query validation error. Because the retryer only auto-recurses on RATE_LIMITED, other rate-limit-adjacent or permission errors reach this branch.
Common situations: A token missing the scopes needed for the stats query; a deprecated GraphQL field after an API change; or a non-NOT_FOUND server-side error from GitHub. Using res.statusText as the type is a smell — the 'code' is not a stable identifier.
Related errors
AI-assisted analysis of anuraghazra/github-readme-stats@54a7985aee (2026-08-12).
Data as JSON: /api/errors/25d7c9dd20b343a5.
Report an issue: GitHub.