{"record":{"id":"ac18e234d367e60f","repo":"github/github-mcp-server","slug":"no-pem-block-found-in-private-key","errorCode":null,"errorMessage":"no PEM block found in private key","messagePattern":"no PEM block found in private key","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"internal/githubapp/githubapp.go","lineNumber":67,"sourceCode":"\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}\n\tkey, ok := parsed.(*rsa.PrivateKey)\n\tif !ok {\n\t\treturn nil, fmt.Errorf(\"private key is %T, want an RSA key\", parsed)\n\t}\n\treturn key, nil\n}\n\nfunc mintJWT(appID string, privateKey *rsa.PrivateKey, now time.Time) (string, error) {\n\theader := map[string]string{\"alg\": \"RS256\", \"typ\": \"JWT\"}\n\tclaims := map[string]any{","sourceCodeStart":49,"sourceCodeEnd":85,"githubUrl":"https://github.com/github/github-mcp-server/blob/0ea1f775a7c73eff1bd2e25904d01136756bbfe2/internal/githubapp/githubapp.go#L49-L85","documentation":"Activity.Unstar returns 204 on success; on any other status the handler drains resp.Body via io.ReadAll to construct the status error, and this error indicates the drain failed. The transport broke after the status line arrived, so the actual API error (404 unknown repo, 401, 403) is unrecoverable from the response. Same failure class as other body-read errors: resets, proxy closes, consumed streams.","triggerScenarios":"Unstar replies 404 (repo deleted or renamed) and the connection drops mid-error-body; proxy resets the stream; pooled keep-alive connection reaped between status and body.","commonSituations":"Bulk unstar scripts over unstable networks; proxies with short idle timeouts; repos renamed between listing and unstarring.","solutions":["Retry unstar_repository once - it is idempotent (204 whether it removed a star or there was none)","Confirm owner/repo still exists (renames produce 404) with a read call","Tune proxy/LB timeouts if recurrent","Capture status codes to distinguish genuine 404s from transport faults"],"exampleFix":"// before\nerr := unstarRepo(ctx, owner, repo)\n\n// after: classify and retry once\nerr := unstarRepo(ctx, owner, repo)\nif isBodyReadError(err) {\n\ttime.Sleep(250 * time.Millisecond)\n\terr = unstarRepo(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":"// unstar_repository is idempotent (204 whether or not a star existed).\nerr := unstarRepo(ctx, owner, repo)\nif isBodyReadError(err) {\n\ttime.Sleep(250 * time.Millisecond)\n\terr = unstarRepo(ctx, owner, repo)\n}","preventionTips":["A retried unstar cannot remove anything extra - retry safely","Handle renamed/deleted repos (404) separately from transport faults","Keep bulk unstar scripts resilient to transient network resets","Cap retries and log the pre-read status code for diagnosis"],"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"}