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

  1. Check GITHUB_TOKEN is set, unexpired, and authorized for the target repo; print only the response.status to diagnose which case you have.
  2. For a classic PAT, grant the 'repo' scope; for a fine-grained PAT, enable repository access plus read access to Discussions and Metadata.
  3. If the token is org-scoped, complete SAML/SSO authorization for the token.
  4. Check https://www.githubstatus.com for ongoing API incidents and retry with backoff for 5xx responses.
  5. 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

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


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)