{"record":{"id":"11938e7dec3cbca7","repo":"github/github-mcp-server","slug":"github-app-private-key-is-required-github-app-pri","errorCode":null,"errorMessage":"GitHub App private key is required (GITHUB_APP_PRIVATE_KEY_PATH or GITHUB_APP_PRIVATE_KEY)","messagePattern":"GitHub App private key is required \\(GITHUB_APP_PRIVATE_KEY_PATH or GITHUB_APP_PRIVATE_KEY\\)","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"internal/githubapp/githubapp.go","lineNumber":57,"sourceCode":"\t// InstallationID identifies the installation whose access token is minted.\n\tInstallationID string\n\n\t// PrivateKeyPEM is the RSA key used to sign app JWTs.\n\tPrivateKeyPEM []byte\n\n\t// BaseRESTURL is the REST API base, e.g. https://api.github.com/ for\n\t// github.com or https://HOST/api/v3/ for GitHub Enterprise Server.\n\tBaseRESTURL string\n}\n\nfunc (c Config) validate() error {\n\tswitch {\n\tcase c.AppID == \"\":\n\t\treturn errors.New(\"GitHub App ID or client ID is required (GITHUB_APP_ID)\")\n\tcase c.InstallationID == \"\":\n\t\treturn errors.New(\"GitHub App installation ID is required (GITHUB_APP_INSTALLATION_ID)\")\n\tcase len(c.PrivateKeyPEM) == 0:\n\t\treturn errors.New(\"GitHub App private key is required (GITHUB_APP_PRIVATE_KEY_PATH or GITHUB_APP_PRIVATE_KEY)\")\n\tcase c.BaseRESTURL == \"\":\n\t\treturn errors.New(\"GitHub App REST base URL is required\")\n\t}\n\treturn nil\n}\n\nfunc parsePrivateKey(pemBytes []byte) (*rsa.PrivateKey, error) {\n\tblock, _ := pem.Decode(pemBytes)\n\tif block == nil {\n\t\treturn nil, errors.New(\"no PEM block found in private key\")\n\t}\n\tif key, err := x509.ParsePKCS1PrivateKey(block.Bytes); err == nil {\n\t\treturn key, nil\n\t}\n\tparsed, err := x509.ParsePKCS8PrivateKey(block.Bytes)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"parsing private key (want PKCS#1 or PKCS#8 RSA): %w\", err)\n\t}","sourceCodeStart":39,"sourceCodeEnd":75,"githubUrl":"https://github.com/github/github-mcp-server/blob/0ea1f775a7c73eff1bd2e25904d01136756bbfe2/internal/githubapp/githubapp.go#L39-L75","documentation":"Activity.Star returns 204 No Content on success; on any other status (404 repo not found, 401, 403) the handler reads resp.Body with io.ReadAll to build the status error. This error means that read failed - a transport fault after the status line arrived, which discards the real API error message. Mid-body connection resets, proxy stream closes, and consumed bodies are the usual causes.","triggerScenarios":"Star replies 404 (owner/repo does not exist) and the connection drops before the error body finishes; a proxy resets the stream; keep-alive race between status and body.","commonSituations":"Scripted starring loops over flaky networks; VPN/proxy instability; LB connection reaping under load.","solutions":["Retry star_repository once - starring is idempotent (204 whether newly starred or already starred)","Verify owner/repo spelling separately, since 404 is the most common non-204 cause","Stabilize proxy/LB idle timeouts if the failure recurs","Log the pre-read status code to separate API errors from transport drops"],"exampleFix":"// before: single attempt fails at body read\nerr := starRepo(ctx, owner, repo)\n\n// after: bounded retry; star is idempotent so a repeat is safe\nerr := starRepo(ctx, owner, repo)\nif isBodyReadError(err) {\n\ttime.Sleep(250 * time.Millisecond)\n\terr = starRepo(ctx, owner, repo)\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":"// star_repository is idempotent (204 whether newly starred or not).\nerr := starRepo(ctx, owner, repo)\nif isBodyReadError(err) {\n\ttime.Sleep(250 * time.Millisecond)\n\terr = starRepo(ctx, owner, repo)\n}","preventionTips":["Rely on the 204 idempotency: a retried star never double-stars","Verify owner/repo spelling before bulk star runs to avoid 404 bodies","Keep star loops on stable network paths; cap retries at 1-2","Log status codes to tell genuine API errors from transport drops"],"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"}