{"record":{"id":"28ac5bb1a5bb2588","repo":"github/github-mcp-server","slug":"failed-to-get-issue-w","errorCode":null,"errorMessage":"failed to get issue: %w","messagePattern":"failed to get issue: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"pkg/github/issues.go","lineNumber":724,"sourceCode":"\t\t\tcase \"get_labels\":\n\t\t\t\tresult, err := GetIssueLabels(ctx, gqlClient, owner, repo, issueNumber)\n\t\t\t\treturn attachIFC(result), nil, err\n\t\t\tdefault:\n\t\t\t\treturn utils.NewToolResultError(fmt.Sprintf(\"unknown method: %s\", method)), nil, nil\n\t\t\t}\n\t\t})\n}\n\nfunc GetIssue(ctx context.Context, client *github.Client, deps ToolDependencies, owner string, repo string, issueNumber int) (*mcp.CallToolResult, error) {\n\tcache, err := deps.GetRepoAccessCache(ctx)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"failed to get repo access cache: %w\", err)\n\t}\n\tflags := deps.GetFlags(ctx)\n\n\tissue, resp, err := client.Issues.Get(ctx, owner, repo, issueNumber)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"failed to get issue: %w\", err)\n\t}\n\tdefer func() { _ = resp.Body.Close() }()\n\n\tif resp.StatusCode != http.StatusOK {\n\t\tbody, err := io.ReadAll(resp.Body)\n\t\tif err != nil {\n\t\t\treturn nil, fmt.Errorf(\"failed to read response body: %w\", err)\n\t\t}\n\t\treturn ghErrors.NewGitHubAPIStatusErrorResponse(ctx, \"failed to get issue\", resp, body), nil\n\t}\n\n\tif flags.LockdownMode {\n\t\tif restricted, err := authorLockdownResult(ctx, cache, owner, repo, issue.GetUser().GetLogin(), lockdownIssueRestrictedMessage); restricted != nil || err != nil {\n\t\t\treturn restricted, err\n\t\t}\n\t}\n\n\t// Sanitize title/body on response","sourceCodeStart":706,"sourceCodeEnd":742,"githubUrl":"https://github.com/github/github-mcp-server/blob/0ea1f775a7c73eff1bd2e25904d01136756bbfe2/pkg/github/issues.go#L706-L742","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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"],"exampleFix":"// before\nissue, resp, err := client.Issues.Get(ctx, owner, repo, n)\nif err != nil {\n    return err // opaque\n}\n\n// after\nissue, resp, err := client.Issues.Get(ctx, owner, repo, n)\nif err != nil {\n    var ghErr *github.ErrorResponse\n    if errors.As(err, &ghErr) {\n        switch ghErr.Response.StatusCode {\n        case http.StatusNotFound:\n            return fmt.Errorf(\"issue %s/%s#%d not found or not visible\", owner, repo, n)\n        case http.StatusUnauthorized, http.StatusForbidden, http.StatusTooManyRequests:\n            return fmt.Errorf(\"auth/rate limit: %v\", ghErr.Message)\n        }\n    }\n    return err\n}\ndefer func() { _ = resp.Body.Close() }()","handlingStrategy":"try-catch","validationCode":"if issueNumber <= 0 {\n    return errors.New(\"issue number must be positive\")\n}\n// optional cheap existence check for private repos\nif _, resp, err := client.Repositories.Get(ctx, owner, repo); err != nil {\n    return fmt.Errorf(\"repo %s/%s not visible to token: %w\", owner, repo, err)\n} else {\n    _ = resp.Body.Close()\n}","typeGuard":null,"tryCatchPattern":"issue, resp, err := client.Issues.Get(ctx, owner, repo, issueNumber)\nif err != nil {\n    var ghErr *github.ErrorResponse\n    if errors.As(err, &ghErr) && ghErr.Response != nil {\n        switch ghErr.Response.StatusCode {\n        case http.StatusNotFound:\n            return fmt.Errorf(\"%s/%s#%d not found or not visible to this token\", owner, repo, issueNumber)\n        case http.StatusUnauthorized:\n            return fmt.Errorf(\"token invalid/expired\")\n        case http.StatusForbidden, http.StatusTooManyRequests:\n            time.Sleep(retryAfter(ghErr.Response)) // honor Retry-After\n            issue, resp, err = client.Issues.Get(ctx, owner, repo, issueNumber)\n            if err != nil {\n                return err\n            }\n        default:\n            return err\n        }\n    }\n    return err\n}\ndefer func() { _ = resp.Body.Close() }()","preventionTips":["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"],"tags":["go","github-api","auth","rate-limit","network"],"backgroundTag":null,"analyzedSha":"0ea1f775a7c73eff1bd2e25904d01136756bbfe2","analyzedAt":"2026-08-15T18:10:19.804Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}