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

  1. Verify the token is valid and not expired (curl -H "Authorization: Bearer $TOKEN" https://api.github.com/user)
  2. Confirm owner, repo, and issueNumber are correct and the token can see the repo (private repo access)
  3. Inspect the wrapped *github.ErrorResponse status and message — 401/403 means fix credentials/authorization, 404 means fix arguments
  4. 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

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


AI-assisted analysis of github/github-mcp-server@0ea1f775a7 (2026-08-15). Data as JSON: /api/errors/60cf88fe451fd9dc. Report an issue: GitHub.