github/github-mcp-server · error · GitHubAPIError
%s: %w
Error message
%s: %w
What it means
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.
Source
Thrown at pkg/errors/error.go:32
)
type GitHubAPIError struct {
Message string `json:"message"`
Response *github.Response `json:"-"`
Err error `json:"-"`
}
// NewGitHubAPIError creates a new GitHubAPIError with the provided message, response, and error.
func newGitHubAPIError(message string, resp *github.Response, err error) *GitHubAPIError {
return &GitHubAPIError{
Message: message,
Response: resp,
Err: err,
}
}
func (e *GitHubAPIError) Error() string {
return fmt.Errorf("%s: %w", e.Message, e.Err).Error()
}
type GitHubGraphQLError struct {
Message string `json:"message"`
Err error `json:"-"`
}
func newGitHubGraphQLError(message string, err error) *GitHubGraphQLError {
return &GitHubGraphQLError{
Message: message,
Err: err,
}
}
func (e *GitHubGraphQLError) Error() string {
return fmt.Errorf("%s: %w", e.Message, e.Err).Error()
}
View on GitHub (pinned to 0ea1f775a7)
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
Example fix
// before
result, err := callTool("list_workflow_runs", args)
if err != nil {
log.Printf("failed: %v", err) // opaque string
}
// after
var ghErr *ghErrors.GitHubAPIError
if errors.As(err, &ghErr) {
switch ghErr.Response.StatusCode {
case 401:
refreshToken() // token expired or invalid
case 403:
waitForRateLimitReset(ghErr.Response.Header) // or missing scope
case 404:
fixOwnerRepo() // wrong owner/repo or resource id
default:
log.Printf("github api error: %s (status %d)", ghErr.Message, ghErr.Response.StatusCode)
}
} Defensive patterns
Strategy: type-guard
Validate before calling
// Cheap pre-flight: verify the token before running REST tools
if _, _, err := client.Users.Get(ctx, ""); err != nil {
return fmt.Errorf("token preflight failed: %w", err)
} Type guard
func asGitHubAPIError(err error) (*ghErrors.GitHubAPIError, bool) {
var ghErr *ghErrors.GitHubAPIError
if errors.As(err, &ghErr) {
return ghErr, true
}
return nil, false
} Try / catch
if err != nil {
if ghErr, ok := asGitHubAPIError(err); ok && ghErr.Response != nil {
switch ghErr.Response.StatusCode {
case 401: // rotate token
case 403: // check rate-limit headers / scopes, back off
case 404: // fix owner/repo/id
default: // log ghErr.Message and inner ghErr.Err
}
}
return err
} Prevention
- 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
When it happens
Trigger: 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.
Common situations: 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.
Related errors
- failed to get issue ID: %w
- failed to get issue: %w
- failed to fetch raw content: %s
- failed to get user permission level: %w
- unexpected status %d: %s
AI-assisted analysis of github/github-mcp-server@0ea1f775a7 (2026-08-15).
Data as JSON: /api/errors/441b08df89468be1.
Report an issue: GitHub.