github/github-mcp-server · error
failed to get issue: %w
Error message
failed to get issue: %w
What it means
GetIssue (pkg/github/issues.go:724) wraps a failure of client.Issues.Get — the REST call GET /repos/{owner}/{repo}/issues/{number} returned a transport error or an HTTP error status. Unlike the structured NewGitHubAPIErrorResponse path used elsewhere, this branch returns a plain wrapped Go error, so the GitHub error details (404 body, rate-limit message) live inside err.
Source
Thrown at pkg/github/issues.go:724
case "get_labels":
result, err := GetIssueLabels(ctx, gqlClient, owner, repo, issueNumber)
return attachIFC(result), nil, err
default:
return utils.NewToolResultError(fmt.Sprintf("unknown method: %s", method)), nil, nil
}
})
}
func GetIssue(ctx context.Context, client *github.Client, deps ToolDependencies, owner string, repo string, issueNumber int) (*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)
issue, resp, err := client.Issues.Get(ctx, owner, repo, issueNumber)
if err != nil {
return nil, fmt.Errorf("failed to get issue: %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", resp, body), nil
}
if flags.LockdownMode {
if restricted, err := authorLockdownResult(ctx, cache, owner, repo, issue.GetUser().GetLogin(), lockdownIssueRestrictedMessage); restricted != nil || err != nil {
return restricted, err
}
}
// Sanitize title/body on responseView on GitHub (pinned to 0ea1f775a7)
Solutions
- Verify owner/repo/issue_number exactly (issue, not PR number where semantics matter) and that the token can see the repo
- Check rate limits: on 403/429 inspect x-ratelimit-remaining and retry-after, then back off
- Handle 301 moved: follow the redirect target or update stored owner/repo after renames/transfers
- For auth errors, refresh the token and confirm SSO authorization for the org
Example fix
// before
issue, resp, err := client.Issues.Get(ctx, owner, repo, n)
if err != nil {
return err // opaque
}
// after
issue, resp, err := client.Issues.Get(ctx, owner, repo, n)
if err != nil {
var ghErr *github.ErrorResponse
if errors.As(err, &ghErr) {
switch ghErr.Response.StatusCode {
case http.StatusNotFound:
return fmt.Errorf("issue %s/%s#%d not found or not visible", owner, repo, n)
case http.StatusUnauthorized, http.StatusForbidden, http.StatusTooManyRequests:
return fmt.Errorf("auth/rate limit: %v", ghErr.Message)
}
}
return err
}
defer func() { _ = resp.Body.Close() }() Defensive patterns
Strategy: try-catch
Validate before calling
if issueNumber <= 0 {
return errors.New("issue number must be positive")
}
// optional cheap existence check for private repos
if _, resp, err := client.Repositories.Get(ctx, owner, repo); err != nil {
return fmt.Errorf("repo %s/%s not visible to token: %w", owner, repo, err)
} else {
_ = resp.Body.Close()
} Try / catch
issue, resp, err := client.Issues.Get(ctx, owner, repo, issueNumber)
if err != nil {
var ghErr *github.ErrorResponse
if errors.As(err, &ghErr) && ghErr.Response != nil {
switch ghErr.Response.StatusCode {
case http.StatusNotFound:
return fmt.Errorf("%s/%s#%d not found or not visible to this token", owner, repo, issueNumber)
case http.StatusUnauthorized:
return fmt.Errorf("token invalid/expired")
case http.StatusForbidden, http.StatusTooManyRequests:
time.Sleep(retryAfter(ghErr.Response)) // honor Retry-After
issue, resp, err = client.Issues.Get(ctx, owner, repo, issueNumber)
if err != nil {
return err
}
default:
return err
}
}
return err
}
defer func() { _ = resp.Body.Close() }() Prevention
- Validate owner/repo/issue_number shape before calling; cheap and catches most 404s
- Honor Retry-After on 403/429 — GitHub secondary limits punish immediate retries hard
- Resolve renames/transfers by following the Location header or re-fetching the repo before issue calls
- Scope tokens to the repos you touch; invisible private repos look identical to nonexistent ones
When it happens
Trigger: 404 because owner, repo, or issue number is wrong (or the repo is private and invisible to the token); 401/403 for expired/insufficient token or blocked by SSO enforcement; 403/429 primary or secondary rate limits; 301 after a repo rename/transfer; network/DNS failure to api.github.com or the GHES host.
Common situations: Wrong owner/repo spelling in tool args; tokens missing repo scope for private repos; bulk automation hitting secondary rate limits; renamed/transferred repositories with stale references; GHES base URL misconfiguration.
Related errors
- failed to get issue ID: %w
- failed to fetch raw content: %s
- %s: %w
- failed to query issue fields metadata: %w
- failed to read response body: %w
AI-assisted analysis of github/github-mcp-server@0ea1f775a7 (2026-08-15).
Data as JSON: /api/errors/28ac5bb1a5bb2588.
Report an issue: GitHub.