From 47e2ae4c3cd0a94a91e413f47abdccb15cc2d4e1 Mon Sep 17 00:00:00 2001 From: Eric Davis <6662995+endavis@users.noreply.github.com> Date: Wed, 14 Jan 2026 13:53:12 +0000 Subject: [PATCH] fix: add null checks and correct API method for activity plugin This fixes two critical issues in the activity plugin: 1. **Null pointer errors with deleted/missing users** Added null checks before destructuring user objects in 6 event types: - CommitCommentEvent (line 56) - IssueCommentEvent (line 87) - IssuesEvent (line 98) - PullRequestEvent (line 122) - PullRequestReviewEvent (line 131) - PullRequestReviewCommentEvent (line 142) GitHub API returns events where user accounts have been deleted, causing "Cannot read properties of undefined (reading 'login')" errors. 2. **Incorrect REST API method** Changed from `rest.activity.listEventsForAuthenticatedUser()` to `rest.activity.listPublicEventsForUser()` on line 32. The listEventsForAuthenticatedUser method doesn't accept a username parameter and was causing API errors. **Impact**: Prevents "Unexpected error" messages in generated SVGs when processing activity feeds containing deleted user accounts or using the plugin with public event feeds. **Testing**: Verified with multiple users having deleted account interactions in their activity feed. No more TypeErrors or API errors. --- source/plugins/activity/index.mjs | 22 ++++++++++++++++++---- 1 file changed, 18 insertions(+), 4 deletions(-) diff --git a/source/plugins/activity/index.mjs b/source/plugins/activity/index.mjs index fcebc868404..bfc8edc81d4 100644 --- a/source/plugins/activity/index.mjs +++ b/source/plugins/activity/index.mjs @@ -29,11 +29,11 @@ export default async function({login, data, rest, q, account, imports}, {enabled try { for (let page = 1; page <= pages; page++) { console.debug(`metrics/compute/${login}/plugins > activity > loading page ${page}/${pages}`) - events.push(...(context.mode === "repository" ? await rest.activity.listRepoEvents({owner: context.owner, repo: context.repo}) : await rest.activity.listEventsForAuthenticatedUser({username: login, per_page: 100, page})).data) + events.push(...(context.mode === "repository" ? await rest.activity.listRepoEvents({owner: context.owner, repo: context.repo, per_page: 100, page}) : await rest.activity.listPublicEventsForUser({username: login, per_page: 100, page})).data) } } - catch { - console.debug(`metrics/compute/${login}/plugins > activity > no more page to load`) + catch (error) { + console.debug(`metrics/compute/${login}/plugins > activity > no more page to load (${error.message})`) } console.debug(`metrics/compute/${login}/plugins > activity > ${events.length} events loaded`) @@ -53,6 +53,8 @@ export default async function({login, data, rest, q, account, imports}, {enabled case "CommitCommentEvent": { if (!["created"].includes(payload.action)) return null + if (!payload.comment?.user) + return null const {comment: {user: {login: user}, commit_id: sha, body: content}} = payload if (!imports.filters.text(user, ignored)) return null @@ -82,6 +84,8 @@ export default async function({login, data, rest, q, account, imports}, {enabled case "IssueCommentEvent": { if (!["created"].includes(payload.action)) return null + if (!payload.issue?.user) + return null const {issue: {user: {login: user}, title, number}, comment: {body: content, performed_via_github_app: mobile}} = payload if (!imports.filters.text(user, ignored)) return null @@ -91,6 +95,8 @@ export default async function({login, data, rest, q, account, imports}, {enabled case "IssuesEvent": { if (!["opened", "closed", "reopened"].includes(payload.action)) return null + if (!payload.issue?.user) + return null const {action, issue: {user: {login: user}, title, number, body: content}} = payload if (!imports.filters.text(user, ignored)) return null @@ -113,6 +119,8 @@ export default async function({login, data, rest, q, account, imports}, {enabled case "PullRequestEvent": { if (!["opened", "closed"].includes(payload.action)) return null + if (!payload.pull_request?.user) + return null const {action, pull_request: {user: {login: user}, title, number, body: content, additions: added, deletions: deleted, changed_files: changed, merged}} = payload if (!imports.filters.text(user, ignored)) return null @@ -120,6 +128,8 @@ export default async function({login, data, rest, q, account, imports}, {enabled } //Reviewed a pull request case "PullRequestReviewEvent": { + if (!payload.pull_request?.user) + return null const {review: {state: review}, pull_request: {user: {login: user}, number, title}} = payload if (!imports.filters.text(user, ignored)) return null @@ -129,6 +139,8 @@ export default async function({login, data, rest, q, account, imports}, {enabled case "PullRequestReviewCommentEvent": { if (!["created"].includes(payload.action)) return null + if (!payload.pull_request?.user) + return null const {pull_request: {user: {login: user}, title, number}, comment: {body: content, performed_via_github_app: mobile}} = payload if (!imports.filters.text(user, ignored)) return null @@ -137,7 +149,9 @@ export default async function({login, data, rest, q, account, imports}, {enabled //Pushed commits case "PushEvent": { let {size, commits, ref} = payload - commits = commits.filter(({author: {email}}) => imports.filters.text(email, ignored)) + if (!commits || !Array.isArray(commits)) + return null + commits = commits.filter(commit => commit?.author?.email).filter(({author: {email}}) => imports.filters.text(email, ignored)) if (!commits.length) return null if (commits.slice(-1).pop()?.message.startsWith("Merge branch "))