github/github-mcp-server · error
failed to get issue comments: %w
Error message
failed to get issue comments: %w
What it means
The go-github Issues.ListComments call returned a non-nil error. go-github converts transport failures (DNS, TLS, connection reset) and non-2xx HTTP responses (401 bad credentials, 403 forbidden/SAML/rate limit, 404 not found, 422 validation) into *github.ErrorResponse, which this tool wraps and returns to the MCP caller unchanged.
Source
Thrown at pkg/github/issues.go:849
}
func GetIssueComments(ctx context.Context, client *github.Client, deps ToolDependencies, owner string, repo string, issueNumber int, pagination PaginationParams) (*mcp.CallToolResult, error) {
cache, err := deps.GetRepoAccessCache(ctx)
if err != nil {
return nil, fmt.Errorf("failed to get repo access cache: %w", err)
}
flags := deps.GetFlags(ctx)
opts := &github.IssueListCommentsOptions{
ListOptions: github.ListOptions{
Page: pagination.Page,
PerPage: pagination.PerPage,
},
}
comments, resp, err := client.Issues.ListComments(ctx, owner, repo, issueNumber, opts)
if err != nil {
return nil, fmt.Errorf("failed to get issue comments: %w", err)
}
defer func() { _ = resp.Body.Close() }()
if resp.StatusCode != http.StatusOK {
body, err := io.ReadAll(resp.Body)
if err != nil {
return nil, fmt.Errorf("failed to read response body: %w", err)
}
return ghErrors.NewGitHubAPIStatusErrorResponse(ctx, "failed to get issue comments", resp, body), nil
}
if flags.LockdownMode {
if cache == nil {
return nil, fmt.Errorf("lockdown cache is not configured")
}
filteredComments := make([]*github.IssueComment, 0, len(comments))
for _, comment := range comments {
user := comment.User
if user == nil {View on GitHub (pinned to 0ea1f775a7)
Solutions
- Verify the token is valid and not expired (curl -H "Authorization: Bearer $TOKEN" https://api.github.com/user)
- Confirm owner, repo, and issueNumber are correct and the token can see the repo (private repo access)
- Inspect the wrapped *github.ErrorResponse status and message — 401/403 means fix credentials/authorization, 404 means fix arguments
- Retry with exponential backoff only for 5xx statuses or transport errors; never retry 4xx
Example fix
// before — no discrimination between error kinds
comments, err := github.GetIssueComments(ctx, client, deps, owner, repo, num, pagination)
// after — branch on the underlying GitHub error
var ghErr *github.ErrorResponse
if errors.As(err, &ghErr) {
switch ghErr.Response.StatusCode {
case http.StatusUnauthorized, http.StatusForbidden:
return fmt.Errorf("check token/scopes: %w", err)
case http.StatusNotFound:
return fmt.Errorf("repo/issue not found or inaccessible: %w", err)
}
}
return err // transport error — safe to retry Defensive patterns
Strategy: retry
Validate before calling
// Validate inputs and credentials before the API call
if owner == "" || repo == "" || issueNumber <= 0 {
return fmt.Errorf("owner, repo and a positive issue number are required")
}
if pagination.PerPage < 1 || pagination.PerPage > 100 {
pagination.PerPage = 30 // clamp to API limits
} Type guard
var ghErr *github.ErrorResponse
func isGitHubAPIError(err error) bool { return errors.As(err, &ghErr) } Try / catch
comments, err := githubpkg.GetIssueComments(ctx, client, deps, owner, repo, num, pagination)
if err != nil {
var ghErr *github.ErrorResponse
if errors.As(err, &ghErr) {
switch ghErr.Response.StatusCode {
case http.StatusUnauthorized, http.StatusForbidden:
return fmt.Errorf("credentials/authorization problem: %w", err) // no retry
case http.StatusNotFound:
return fmt.Errorf("no such repo/issue (or no access): %w", err) // no retry
}
}
// transport error or 5xx — bounded retry
return retryWithBackoff(ctx, 3, func() error { return retryCall() })
} Prevention
- Use short-lived, auto-rotating tokens so 401s surface as auth events, not mystery failures
- Pre-validate owner/repo shape before invoking the tool
- Respect X-RateLimit-Remaining headers to avoid 403 secondary limits
- Differentiate retryable (5xx, network) from permanent (4xx) failures before retrying
When it happens
Trigger: GET /repos/{owner}/{repo}/issues/{number}/comments fails at transport level (network down, DNS failure, TLS handshake error) or returns 401 (expired/revoked token), 404 (wrong owner/repo or issue number, or no access to a private repo), 403 (missing SAML authorization, secondary rate limit).
Common situations: Expired personal access token; typo'd owner/repo arguments from the LLM host; token without access to the target private repo; GitHub secondary rate limits on aggressive comment polling; GitHub outage returning 5xx.
Related errors
- failed to get issue: %w
- failed to read response body: %w
- invalid issue URL %q: %w
- GitHub App authentication requires a private key: set GITHUB
- GitHub App private key is required (GITHUB_APP_PRIVATE_KEY_P
AI-assisted analysis of github/github-mcp-server@0ea1f775a7 (2026-08-15).
Data as JSON: /api/errors/60cf88fe451fd9dc.
Report an issue: GitHub.