{"record":{"id":"98b6b36b791058c9","repo":"oven-sh/bun","slug":"failed-to-count-completed-issues-error","errorCode":null,"errorMessage":"Failed to count completed issues: ${error}","messagePattern":"Failed to count completed issues: (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"scripts/github-metrics.ts","lineNumber":54,"sourceCode":"  }\n}\n\n/**\n * Count issues closed as completed since a given date\n */\nasync function countCompletedIssues(sinceDate: string): Promise<{ count: number; issues: number[] }> {\n  try {\n    const result =\n      (await $`gh issue list --state closed --search \"closed:>=${sinceDate} reason:completed\" --limit 1000 --json number,closedAt,stateReason`.json()) as Issue[];\n\n    const completedIssues = result.filter(issue => issue.stateReason === \"COMPLETED\");\n\n    return {\n      count: completedIssues.length,\n      issues: completedIssues.map(issue => issue.number),\n    };\n  } catch (error) {\n    throw new Error(`Failed to count completed issues: ${error}`);\n  }\n}\n\n/**\n * Get positive reactions for an issue (👍, ❤️, 🎉, 🚀)\n */\nasync function getIssueReactions(issueNumber: number): Promise<number> {\n  try {\n    const reactions = (await $`gh api \"repos/oven-sh/bun/issues/${issueNumber}/reactions\"`.json()) as Reaction[];\n    return reactions.filter(r => [\"+1\", \"heart\", \"hooray\", \"rocket\"].includes(r.content)).length;\n  } catch {\n    return 0;\n  }\n}\n\n/**\n * Get positive reactions for all comments on an issue\n */","sourceCodeStart":36,"sourceCodeEnd":72,"githubUrl":"https://github.com/oven-sh/bun/blob/8c5296ac459e8252d3cd702f3fbcbb0c249d95d5/scripts/github-metrics.ts#L36-L72","documentation":"Thrown by countCompletedIssues() when the `gh issue list --state closed --search \"closed:>=<date> reason:completed\" --limit 1000 --json ...` invocation fails. It wraps the gh CLI error, so the real cause (query syntax, auth, rate limit, repo context) is in the appended message.","triggerScenarios":"sinceDate not in the YYYY-MM-DD form gh's search qualifier expects; gh unauthenticated or its token expired; secondary rate limit from repeated --limit 1000 queries; running outside a git checkout without GH_REPO set.","commonSituations":"Passing an ISO timestamp or Date object string instead of a plain date; CI where GH_TOKEN isn't in env; running the script in a directory that isn't the bun repo so gh can't infer oven-sh/bun.","solutions":["Run the exact gh command manually to see the raw error: `gh issue list --state closed --search 'closed:>=2026-01-01 reason:completed' --limit 1000 --json number`","Normalize sinceDate to YYYY-MM-DD before calling","Run `gh auth status`; export GH_TOKEN or set GH_REPO=oven-sh/bun","Retry after the rate-limit window if gh reports a secondary rate limit"],"exampleFix":"// before\nconst { count, issues } = await countCompletedIssues(sinceDate);\n\n// after\nconst since = new Date(sinceDate).toISOString().slice(0, 10);\nif (!/^\\d{4}-\\d{2}-\\d{2}$/.test(since)) throw new Error(`bad sinceDate: ${sinceDate}`);\nconst { count, issues } = await countCompletedIssues(since);","handlingStrategy":"retry","validationCode":"// gh search qualifiers need a plain YYYY-MM-DD date\nif (!/^\\d{4}-\\d{2}-\\d{2}$/.test(sinceDate)) {\n  throw new Error(`sinceDate must be YYYY-MM-DD, got: ${sinceDate}`);\n}\nawait $`gh auth status`.quiet(); // fail fast on auth problems","typeGuard":null,"tryCatchPattern":"let result;\nfor (let attempt = 0; attempt < 3; attempt++) {\n  try {\n    result = await countCompletedIssues(sinceDate);\n    break;\n  } catch (error) {\n    if (/rate limit/i.test(String(error)) && attempt < 2) continue;\n    throw error;\n  }\n}","preventionTips":["Normalize dates to YYYY-MM-DD before building gh search queries","Set GH_REPO=oven-sh/bun when running outside the repo checkout","Cache gh results in CI to avoid repeated --limit 1000 queries"],"tags":["github-cli","issues","ci","network"],"backgroundTag":null,"analyzedSha":"8c5296ac459e8252d3cd702f3fbcbb0c249d95d5","analyzedAt":"2026-08-16T08:01:58.794Z","schemaVersion":2},"datasetVersion":"2026-08-16T08:17:34.114Z"}