{"record":{"id":"441b08df89468be1","repo":"github/github-mcp-server","slug":"s-w","errorCode":null,"errorMessage":"%s: %w","messagePattern":"%s: %w","errorType":"http","errorClass":"GitHubAPIError","httpStatus":null,"severity":"error","filePath":"pkg/errors/error.go","lineNumber":32,"sourceCode":")\n\ntype GitHubAPIError struct {\n\tMessage  string           `json:\"message\"`\n\tResponse *github.Response `json:\"-\"`\n\tErr      error            `json:\"-\"`\n}\n\n// NewGitHubAPIError creates a new GitHubAPIError with the provided message, response, and error.\nfunc newGitHubAPIError(message string, resp *github.Response, err error) *GitHubAPIError {\n\treturn &GitHubAPIError{\n\t\tMessage:  message,\n\t\tResponse: resp,\n\t\tErr:      err,\n\t}\n}\n\nfunc (e *GitHubAPIError) Error() string {\n\treturn fmt.Errorf(\"%s: %w\", e.Message, e.Err).Error()\n}\n\ntype GitHubGraphQLError struct {\n\tMessage string `json:\"message\"`\n\tErr     error  `json:\"-\"`\n}\n\nfunc newGitHubGraphQLError(message string, err error) *GitHubGraphQLError {\n\treturn &GitHubGraphQLError{\n\t\tMessage: message,\n\t\tErr:     err,\n\t}\n}\n\nfunc (e *GitHubGraphQLError) Error() string {\n\treturn fmt.Errorf(\"%s: %w\", e.Message, e.Err).Error()\n}\n","sourceCodeStart":14,"sourceCodeEnd":50,"githubUrl":"https://github.com/github/github-mcp-server/blob/0ea1f775a7c73eff1bd2e25904d01136756bbfe2/pkg/errors/error.go#L14-L50","documentation":"Error() method of GitHubAPIError (pkg/errors/error.go:32), the wrapper this server creates for every failed go-github REST API call. The rendered text is \"<message>: <cause>\"; seeing it means a REST request (Actions, Issues, Repos, ...) already failed and the wrapper is being printed or logged. The struct retains the *github.Response for status/header inspection and the embedded Err supports errors.Is/errors.As unwrapping.","triggerScenarios":"Any REST API failure surfaced through NewGitHubAPIErrorResponse: 401 invalid/expired PAT, 403 primary or secondary rate limit, 404 wrong owner/repo/resource, 5xx GitHub outages, or network-level errors from go-github. Formatting this error is the last step of every failed REST tool call.","commonSituations":"Expired personal access token; fine-grained PAT missing the 'Actions: Read' or 'Contents: Read' permission; rate-limit exhaustion in tight agent loops; GHES base URL misconfigured so API calls hit the wrong host; token scopes stripped by a proxy.","solutions":["Do not string-match the message - unwrap with errors.As(err, *GitHubAPIError) and switch on Response.StatusCode","401: refresh/replace the PAT; 403: check X-RateLimit-Remaining headers and back off; 404: verify owner/repo and resource IDs","For fine-grained tokens, grant the specific repository permissions the tool needs","If e.Err is nil you may see '%!w(<nil>)' - treat it as an error-assembly bug and report it upstream"],"exampleFix":"// before\nresult, err := callTool(\"list_workflow_runs\", args)\nif err != nil {\n    log.Printf(\"failed: %v\", err) // opaque string\n}\n\n// after\nvar ghErr *ghErrors.GitHubAPIError\nif errors.As(err, &ghErr) {\n    switch ghErr.Response.StatusCode {\n    case 401:\n        refreshToken() // token expired or invalid\n    case 403:\n        waitForRateLimitReset(ghErr.Response.Header) // or missing scope\n    case 404:\n        fixOwnerRepo() // wrong owner/repo or resource id\n    default:\n        log.Printf(\"github api error: %s (status %d)\", ghErr.Message, ghErr.Response.StatusCode)\n    }\n}","handlingStrategy":"type-guard","validationCode":"// Cheap pre-flight: verify the token before running REST tools\nif _, _, err := client.Users.Get(ctx, \"\"); err != nil {\n    return fmt.Errorf(\"token preflight failed: %w\", err)\n}","typeGuard":"func asGitHubAPIError(err error) (*ghErrors.GitHubAPIError, bool) {\n    var ghErr *ghErrors.GitHubAPIError\n    if errors.As(err, &ghErr) {\n        return ghErr, true\n    }\n    return nil, false\n}","tryCatchPattern":"if err != nil {\n    if ghErr, ok := asGitHubAPIError(err); ok && ghErr.Response != nil {\n        switch ghErr.Response.StatusCode {\n        case 401: // rotate token\n        case 403: // check rate-limit headers / scopes, back off\n        case 404: // fix owner/repo/id\n        default: // log ghErr.Message and inner ghErr.Err\n        }\n    }\n    return err\n}","preventionTips":["Never string-match error text; always errors.As to the typed wrapper","Set token expiry reminders or use short-lived auto-refreshed PATs","Respect X-RateLimit-Remaining and sleep toward X-RateLimit-Reset in loops","Grant fine-grained PATs only the permissions the tool set needs"],"tags":["github-api","rest","error-wrapping","auth","go"],"backgroundTag":null,"analyzedSha":"0ea1f775a7c73eff1bd2e25904d01136756bbfe2","analyzedAt":"2026-08-15T18:10:19.804Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}