{"record":{"id":"e2f62b0f40558407","repo":"github/github-mcp-server","slug":"github-app-authentication-requires-a-private-key","errorCode":null,"errorMessage":"GitHub App authentication requires a private key: set GITHUB_APP_PRIVATE_KEY_PATH (preferred) or GITHUB_APP_PRIVATE_KEY","messagePattern":"GitHub App authentication requires a private key: set GITHUB_APP_PRIVATE_KEY_PATH \\(preferred\\) or GITHUB_APP_PRIVATE_KEY","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"cmd/github-mcp-server/main.go","lineNumber":353,"sourceCode":"\t}, nil)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"failed to configure GitHub App authentication: %w\", err)\n\t}\n\treturn provider.AccessToken, nil\n}\n\nfunc loadAppPrivateKey(path, inline string) ([]byte, error) {\n\tswitch {\n\tcase path != \"\":\n\t\tdata, err := os.ReadFile(path) //#nosec G304 -- operator-supplied path to their own key\n\t\tif err != nil {\n\t\t\treturn nil, fmt.Errorf(\"reading GitHub App private key file: %w\", err)\n\t\t}\n\t\treturn data, nil\n\tcase inline != \"\":\n\t\treturn []byte(strings.ReplaceAll(inline, `\\n`, \"\\n\")), nil\n\tdefault:\n\t\treturn nil, errors.New(\"GitHub App authentication requires a private key: set GITHUB_APP_PRIVATE_KEY_PATH (preferred) or GITHUB_APP_PRIVATE_KEY\")\n\t}\n}\n\nfunc wordSepNormalizeFunc(_ *pflag.FlagSet, name string) pflag.NormalizedName {\n\tfrom := []string{\"_\"}\n\tto := \"-\"\n\tfor _, sep := range from {\n\t\tname = strings.ReplaceAll(name, sep, to)\n\t}\n\treturn pflag.NormalizedName(name)\n}\n","sourceCodeStart":335,"sourceCodeEnd":365,"githubUrl":"https://github.com/github/github-mcp-server/blob/0ea1f775a7c73eff1bd2e25904d01136756bbfe2/cmd/github-mcp-server/main.go#L335-L365","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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"],"exampleFix":"// before: error propagates with no context\nrepos, err := listStarred(ctx, username)\n\n// after: classify and retry transient body-read failures once\nrepos, err := listStarred(ctx, username)\nif isBodyReadError(err) {\n\ttime.Sleep(250 * time.Millisecond)\n\trepos, err = listStarred(ctx, username)\n}","handlingStrategy":"retry","validationCode":null,"typeGuard":"func isBodyReadError(err error) bool {\n\treturn err != nil && strings.Contains(err.Error(), \"failed to read response body\")\n}","tryCatchPattern":"// list_starred_repositories is a read: retry safely.\nrepos, err := listStarred(ctx, username)\nif isBodyReadError(err) {\n\ttime.Sleep(250 * time.Millisecond)\n\trepos, err = listStarred(ctx, username)\n}","preventionTips":["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"],"tags":["go","network","io","transient"],"backgroundTag":null,"analyzedSha":"0ea1f775a7c73eff1bd2e25904d01136756bbfe2","analyzedAt":"2026-08-15T18:10:19.804Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}