github/github-mcp-server · warning
GitHub App authentication requires a private key: set GITHUB
Error message
GitHub App authentication requires a private key: set GITHUB_APP_PRIVATE_KEY_PATH (preferred) or GITHUB_APP_PRIVATE_KEY
What it means
After Activity.ListStarred returns a non-200 status (404 for an unknown username, 401/403 for auth problems), the handler reads resp.Body with io.ReadAll to build the status error response; this error means that read failed. The failure is transport-level and occurs after headers arrived, so the underlying API error text is lost. Causes include mid-body connection resets, proxies closing streams, and already-consumed bodies.
Source
Thrown at cmd/github-mcp-server/main.go:353
}, nil)
if err != nil {
return nil, fmt.Errorf("failed to configure GitHub App authentication: %w", err)
}
return provider.AccessToken, nil
}
func loadAppPrivateKey(path, inline string) ([]byte, error) {
switch {
case path != "":
data, err := os.ReadFile(path) //#nosec G304 -- operator-supplied path to their own key
if err != nil {
return nil, fmt.Errorf("reading GitHub App private key file: %w", err)
}
return data, nil
case inline != "":
return []byte(strings.ReplaceAll(inline, `\n`, "\n")), nil
default:
return nil, errors.New("GitHub App authentication requires a private key: set GITHUB_APP_PRIVATE_KEY_PATH (preferred) or GITHUB_APP_PRIVATE_KEY")
}
}
func wordSepNormalizeFunc(_ *pflag.FlagSet, name string) pflag.NormalizedName {
from := []string{"_"}
to := "-"
for _, sep := range from {
name = strings.ReplaceAll(name, sep, to)
}
return pflag.NormalizedName(name)
}
View on GitHub (pinned to 0ea1f775a7)
Solutions
- Retry list_starred_repositories - it is a read, safe to repeat
- If it recurs, raise proxy/LB idle timeouts or disable aggressive connection reaping on the API path
- Log the status code seen before the read failed to distinguish API 404/403 from transport faults
- Report persistent occurrences with request ID; truncated error bodies hide the real API message
Example fix
// before: error propagates with no context
repos, err := listStarred(ctx, username)
// after: classify and retry transient body-read failures once
repos, err := listStarred(ctx, username)
if isBodyReadError(err) {
time.Sleep(250 * time.Millisecond)
repos, err = listStarred(ctx, username)
} Defensive patterns
Strategy: retry
Type guard
func isBodyReadError(err error) bool {
return err != nil && strings.Contains(err.Error(), "failed to read response body")
} Try / catch
// list_starred_repositories is a read: retry safely.
repos, err := listStarred(ctx, username)
if isBodyReadError(err) {
time.Sleep(250 * time.Millisecond)
repos, err = listStarred(ctx, username)
} Prevention
- Keep client-side pagination state so a retried page is cheap
- Stabilize proxy/VPN links before running large starred-list imports
- Distinguish 404-for-unknown-user from transport faults via status logging
- Cap retries; repeated body-read failures indicate a network defect
When it happens
Trigger: ListStarred replies 404 (user does not exist) or 403 and the connection drops before the JSON error body is fully read; a service mesh resets the stream; a keep-alive pooled connection is reaped between status and body.
Common situations: Unstable VPN/proxy links; LB idle timeouts shorter than body transfer; large starred lists where the error body arrives late; retry storms over half-closed connections.
Understand the failure class
- Authentication and authorization failures — expired tokens, bad credentials, and missing scopes.
Related errors
- GitHub App private key is required (GITHUB_APP_PRIVATE_KEY_P
- no PEM block found in private key
- failed to read log content: %w
- failed to read response body: %w
- failed to read response body: %w
AI-assisted analysis of github/github-mcp-server@0ea1f775a7 (2026-08-15).
Data as JSON: /api/errors/e2f62b0f40558407.
Report an issue: GitHub.