fix: handle null files field from GraphQL on very large PRs (#1593)

GitHub's GraphQL API returns files: null (with no errors entry, and
changedFiles misreported as 0) when a PR's diff is too large to compute.
The unguarded pullRequest.files.nodes dereference in the fetcher crashed
the action with 'TypeError: null is not an object', and the formatter had
the same latent crash on prData.files.nodes.length.

Widen the GitHubPullRequest type to files | null so the compiler enforces
guards, degrade gracefully in the fetcher with a warning, and render the
file count as unavailable (not '0 files') in the formatter.

Fixes #1587
This commit is contained in:
leepokai
2026-08-07 07:55:10 -07:00
committed by GitHub
parent 6ef6450f51
commit 0a5f191964
5 changed files with 92 additions and 3 deletions
+43
View File
@@ -1775,6 +1775,49 @@ describe("fetchGitHubData integration with time filtering", () => {
// Webhook says no body at trigger time — attacker-added GraphQL body must not be used
expect(result.contextData.body).toBe("");
});
it("should not crash when GraphQL returns null files for a very large PR", async () => {
// GitHub declines to compute the diff for very large PRs: `files` comes
// back as null (with no errors entry) and `changedFiles` is misreported
// as 0. The fetch must degrade gracefully instead of throwing.
const mockOctokits = {
graphql: jest.fn().mockResolvedValue({
repository: {
pullRequest: {
number: 7912,
title: "Very large PR",
body: "PR body",
author: { login: "author" },
createdAt: "2024-01-15T10:00:00Z",
state: "OPEN",
labels: { nodes: [] },
comments: { nodes: [] },
files: null,
reviews: { nodes: [] },
},
},
user: { login: "trigger-user" },
}),
rest: {
pulls: {
listFiles: jest.fn().mockResolvedValue({ data: [] }),
},
},
};
const result = await fetchGitHubData({
octokits: mockOctokits as any,
repository: "test-owner/test-repo",
prNumber: "7912",
isPR: true,
triggerUsername: "trigger-user",
triggerTime: "2024-01-15T12:00:00Z",
});
// No file list is available, so the PR is processed without file-level context.
expect(result.changedFiles).toEqual([]);
expect(result.changedFilesWithSHA).toEqual([]);
});
});
describe("filterCommentsByActor", () => {