affaan-m/ECC · error · Error
GitHub GraphQL request failed
Error message
GitHub GraphQL request failed (${response.status}) What it means
githubGraphql wraps the GitHub GraphQL API call and throws a templated error when the HTTP response is not ok, embedding the status code (e.g. 401, 403, 502). GraphQL-level errors returned inside a 200 response are handled separately ('GitHub GraphQL returned errors'); this error specifically means the transport request itself failed.
Solutions
- Check GITHUB_TOKEN is set, unexpired, and authorized for the target repo; print only the response.status to diagnose which case you have.
- For a classic PAT, grant the 'repo' scope; for a fine-grained PAT, enable repository access plus read access to Discussions and Metadata.
- If the token is org-scoped, complete SAML/SSO authorization for the token.
- Check https://www.githubstatus.com for ongoing API incidents and retry with backoff for 5xx responses.
- Verify the endpoint is api.github.com/graphql and the Authorization header format 'Bearer <token>' is intact.
Example fix
// before
headers: { Authorization: `Bearer ${env.GITHUB_TOKEN}`, ... }
// after (fail fast with a clear message when the token is absent)
if (!env.GITHUB_TOKEN) throw new Error('GITHUB_TOKEN is not set; cannot query GitHub GraphQL');
headers: { Authorization: `Bearer ${env.GITHUB_TOKEN}`, ... } Defensive patterns
Strategy: try-catch
Validate before calling
function hasGitHubToken() {
return typeof process.env.GITHUB_TOKEN === 'string' && process.env.GITHUB_TOKEN.trim().length > 0;
}
if (!hasGitHubToken()) throw new Error('GITHUB_TOKEN is required before calling githubGraphql'); Try / catch
try {
const data = await githubGraphql(query, variables);
} catch (err) {
const m = err.message.match(/GitHub GraphQL request failed \((\d+)\)/);
if (m) {
const status = Number(m[1]);
if (status === 401 || status === 403) console.error('Check GITHUB_TOKEN validity, scopes, and SSO authorization');
else if (status >= 500) console.error('GitHub-side error; retry with backoff');
}
throw err;
} Prevention
- Validate the token's scopes (repo, or Discussions read on fine-grained PATs) before the run.
- Authorize org tokens via SAML SSO.
- Monitor githubstatus.com and add backoff for 5xx responses.
- Log only the status code, never the token, when diagnosing failures.
When it happens
Trigger: GITHUB_TOKEN is missing/invalid/expired (401), the token lacks the Discussions read scope or repo access (403), GitHub is returning 5xx during an incident, or request() hit a final 429 that propagates through as a non-ok response.
Common situations: Fine-grained PAT without Discussions permission; classic token without the 'repo' scope on a private repo; org token with SSO not authorized; GitHub status degradation; workflow using GITHUB_TOKEN on an event where discussions are inaccessible.
Understand the failure class
Background: "API error: {status}" and "HTTP 401/403/404/429/5xx" errors: non-2xx HTTP responses explained — this error's family across 27 libraries.
Related errors
- gh pr create failed
- request retry budget exhausted
- announcement has a pending receipt; inspect Discord before…
- AuthenticationError(msg, provider=ProviderType.OPENAI) from…
- Control-plane request failed
AI-assisted analysis of affaan-m/ECC@8321021c54 (2026-09-16).
Data as JSON: /api/errors/27c6381b6a829a70.
Report an issue: GitHub.
Appendix: source
Thrown at scripts/discord/release-announce.mjs:35
const sleep = ms => new Promise(resolve => setTimeout(resolve, ms));
async function request(url, options = {}, attempts = 3) {
for (let attempt = 1; attempt <= attempts; attempt += 1) {
const response = await fetch(url, options);
if (response.status !== 429 || attempt === attempts) return response;
const data = await response.json().catch(() => ({}));
await sleep(Math.min(Number(data.retry_after || 1) * 1000 + 250, 10_000));
}
throw new Error('request retry budget exhausted');
}
async function githubGraphql(query, variables) {
const response = await request('https://api.github.com/graphql', {
method: 'POST',
headers: { Authorization: `Bearer ${env.GITHUB_TOKEN}`, 'Content-Type': 'application/json' },
body: JSON.stringify({ query, variables }),
});
if (!response.ok) throw new Error(`GitHub GraphQL request failed (${response.status})`);
const payload = await response.json();
if (payload.errors) throw new Error('GitHub GraphQL returned errors');
return payload.data;
}
async function releaseFromGitHub() {
const [owner, repo] = env.GITHUB_REPOSITORY.split('/');
const tag = env.RELEASE_TAG || env.GITHUB_REF_NAME;
const response = await request(`https://api.github.com/repos/${owner}/${repo}/releases/tags/${encodeURIComponent(tag)}`, {
headers: { Authorization: `Bearer ${env.GITHUB_TOKEN}`, Accept: 'application/vnd.github+json' },
});
if (!response.ok) throw new Error(`release lookup failed (${response.status})`);
return response.json();
}
async function createOrFindReleaseDiscussion() {
const release = await releaseFromGitHub();
const [owner, name] = env.GITHUB_REPOSITORY.split('/');View on GitHub (pinned to 8321021c54)