affaan-m/ECC · error · Error
discussion receipt lookup failed
Error message
discussion receipt lookup failed
What it means
findReceiptComment pages through a discussion's comments via GraphQL node(id) and throws this when `data.node?.comments` is missing. That means the node lookup returned null/undefined or lacked comments — typically because the discussionId is invalid, the token lacks read:discussion scope, or the node is not a Discussion the caller can see.
Solutions
- Verify the discussionId (node_id) is correct with a direct query: node(id:...) { ... on Discussion { id number } }
- Confirm the token has read:discussion scope for the repository
- Log the raw GraphQL response (data and errors) to see whether node is null vs comments missing
- Fall back to the REST API GET /repos/{owner}/{repo}/discussions/{number}/comments to cross-check
Example fix
// before
const comments = data.node?.comments;
if (!comments) throw new Error('discussion receipt lookup failed');
// after
if (!data.node) throw new Error(`discussion node ${discussionId} not found or not readable`);
const comments = data.node.comments;
if (!comments) throw new Error(`discussion ${discussionId} returned no comments field: ${JSON.stringify(data)}`); Defensive patterns
Strategy: try-catch
Validate before calling
// verify the node resolves before paging comments
const probe = await githubGraphql('query($id:ID!){node(id:$id){id ... on Discussion{number}}}', { id: discussionId });
if (!probe.node) throw new Error(`Discussion node ${discussionId} not readable — check id and token scopes`); Type guard
function isDiscussionComments(node) {
return node != null && typeof node === 'object'
&& node.comments != null
&& Array.isArray(node.comments.nodes)
&& typeof node.comments.pageInfo?.hasNextPage === 'boolean';
} Try / catch
try {
const receipt = await findReceiptComment(discussionId, marker);
} catch (err) {
if (err.message === 'discussion receipt lookup failed') {
console.error(`Node ${discussionId} unreadable: check node_id validity and read:discussion scope`);
}
throw err;
} Prevention
- Persist the discussion node_id from the create mutation and reuse it unchanged
- Grant read:discussion scope to the token
- Include GraphQL error details in githubGraphql's thrown error to expose in-band failures
- Cross-check with the REST endpoint /discussions/{number}/comments when in doubt
When it happens
Trigger: githubGraphql succeeded at the HTTP level but data.node is null (invalid node ID, insufficient permission) or node exists but `comments` field errored/was omitted, so the optional chain yields undefined.
Common situations: A discussion ID from another repository or a stale ID from deleted discussions; token missing the read:discussion scope so node(id) returns null; GitHub returning partial results with in-band errors silently swallowed by the payload.errors check upstream being bypassed is impossible — but a malformed alias/query would produce missing fields.
Understand the failure class
Background: "invalid response format", "malformed payload", "missing data field": when an API returns 200 but the response shape is wrong — this error's family across 23 libraries.
Related errors
- Announcements discussion category is required
- discussion receipt lookup exceeded page budget
- GitHub GraphQL returned errors
- announcement delivered; pending receipt cleanup requires…
- discussion is not an Announcement
AI-assisted analysis of affaan-m/ECC@8321021c54 (2026-09-16).
Data as JSON: /api/errors/77b9e092ec9358aa.
Report an issue: GitHub.
Appendix: source
Thrown at scripts/discord/release-announce.mjs:113
if (!/^\d+$/.test(env.DISCUSSION_NUMBER || '')) throw new Error('discussion number is invalid');
const response = await request(`https://api.github.com/repos/${env.GITHUB_REPOSITORY}/discussions/${env.DISCUSSION_NUMBER}`, {
headers: { Authorization: `Bearer ${env.GITHUB_TOKEN}`, Accept: 'application/vnd.github+json' },
});
if (!response.ok) throw new Error(`discussion lookup failed (${response.status})`);
const discussion = await response.json();
if (discussion.category?.name !== 'Announcements') throw new Error('discussion is not an Announcement');
return { id: discussion.node_id, title: discussion.title, body: discussion.body, url: discussion.html_url };
}
async function findReceiptComment(discussionId, marker) {
let cursor = null;
for (let page = 0; page < 50; page += 1) {
const data = await githubGraphql(
`query($id:ID!,$after:String){node(id:$id){... on Discussion{comments(first:100,after:$after){nodes{id body author{login}} pageInfo{hasNextPage endCursor}}}}}`,
{ id: discussionId, after: cursor },
);
const comments = data.node?.comments;
if (!comments) throw new Error('discussion receipt lookup failed');
const receipt = findDiscussionReceipt(comments.nodes, marker);
if (receipt) return receipt;
if (!comments.pageInfo.hasNextPage) return null;
cursor = comments.pageInfo.endCursor;
}
throw new Error('discussion receipt lookup exceeded page budget');
}
async function addReceiptComment(discussionId, body) {
const data = await githubGraphql(
`mutation($id:ID!,$body:String!){addDiscussionComment(input:{discussionId:$id,body:$body}){comment{id}}}`,
{ id: discussionId, body },
);
return data.addDiscussionComment.comment.id;
}
async function deleteReceiptComment(commentId) {
await githubGraphql(View on GitHub (pinned to 8321021c54)