affaan-m/ECC · error · Error

GitHub GraphQL returned errors

Error message

GitHub GraphQL returned errors

What it means

githubGraphql wraps GitHub's GraphQL API POST and throws this error when the HTTP response is OK but the JSON payload contains an `errors` array. GitHub returns 200 with `errors` for query-level problems (bad query syntax, missing permissions, invalid node IDs, partial failures). The status check alone cannot catch these, so this guard surfaces them as a normal Error.

Solutions

  1. Log `payload.errors` (temporarily change the throw to include JSON.stringify(payload.errors)) to read GitHub's specific message
  2. Verify GITHUB_TOKEN has `repo`, `read:discussion` (and `write:discussion` for mutations) scopes
  3. Validate the GraphQL query in GitHub's GraphQL explorer with the same token
  4. Check for rate-limit/abuse errors in the errors array and add retry with backoff

Example fix

// before
if (payload.errors) throw new Error('GitHub GraphQL returned errors');
// after
if (payload.errors) throw new Error(`GitHub GraphQL returned errors: ${JSON.stringify(payload.errors)}`);
Defensive patterns

Strategy: try-catch

Validate before calling

// preflight: verify token scopes with a cheap query
const check = await fetch('https://api.github.com/graphql', {
  method: 'POST',
  headers: { Authorization: `Bearer ${process.env.GITHUB_TOKEN}`, 'Content-Type': 'application/json' },
  body: JSON.stringify({ query: 'query{viewer{login}}' }),
});
const body = await check.json();
if (body.errors || !body.data?.viewer?.login) throw new Error(`GraphQL preflight failed: ${JSON.stringify(body.errors)}`);

Type guard

function hasGraphqlErrors(payload) {
  return Array.isArray(payload?.errors) && payload.errors.length > 0;
}

Try / catch

try {
  const data = await githubGraphql(query, variables);
} catch (err) {
  if (String(err.message).includes('GitHub GraphQL returned errors')) {
    console.error('GraphQL errors:', err.message); // inspect scopes/ids/query
  }
  throw err;
}

Prevention

When it happens

Trigger: Any call to githubGraphql where GitHub accepts the request (HTTP 2xx) but the response body has a truthy `payload.errors` array — e.g. malformed GraphQL query, missing `read:discussion` scope for a discussion query, invalid repositoryId/categoryId in a createDiscussion mutation.

Common situations: GITHUB_TOKEN lacks the `repo` or `read:discussion` scope required for discussion queries; a renamed/removed discussion node ID passed to a query; typo in a GraphQL field or variable declaration; rate-limit or abuse-detection errors returned in-band.

Related errors


AI-assisted analysis of affaan-m/ECC@8321021c54 (2026-09-16). Data as JSON: /api/errors/bf71f635030cce90. Report an issue: GitHub.

Appendix: source

Thrown at scripts/discord/release-announce.mjs:37

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('/');
  const marker = releaseMarker(release.tag_name);
  const data = await githubGraphql(

View on GitHub (pinned to 8321021c54)