github/github-mcp-server · error
private key is %T, want an RSA key
Error message
private key is %T, want an RSA key
What it means
parsePrivateKey successfully decoded a PEM block and parsed it as a PKCS#8 key, but the parsed key is not RSA (the %T verb prints the concrete Go type, e.g. *ecdsa.PrivateKey or ed25519.PrivateKey). The JWT in mintJWT is signed with RS256 via rsa.SignPKCS1v15, which requires an *rsa.PrivateKey, so any other algorithm is rejected at load time. GitHub Apps issue RSA PEM files, so a non-RSA key almost always means a self-generated key of the wrong type.
Source
Thrown at internal/githubapp/githubapp.go:78
}
return nil
}
func parsePrivateKey(pemBytes []byte) (*rsa.PrivateKey, error) {
block, _ := pem.Decode(pemBytes)
if block == nil {
return nil, errors.New("no PEM block found in private key")
}
if key, err := x509.ParsePKCS1PrivateKey(block.Bytes); err == nil {
return key, nil
}
parsed, err := x509.ParsePKCS8PrivateKey(block.Bytes)
if err != nil {
return nil, fmt.Errorf("parsing private key (want PKCS#1 or PKCS#8 RSA): %w", err)
}
key, ok := parsed.(*rsa.PrivateKey)
if !ok {
return nil, fmt.Errorf("private key is %T, want an RSA key", parsed)
}
return key, nil
}
func mintJWT(appID string, privateKey *rsa.PrivateKey, now time.Time) (string, error) {
header := map[string]string{"alg": "RS256", "typ": "JWT"}
claims := map[string]any{
"iat": now.Add(-clockSkew).Unix(),
"exp": now.Add(jwtLifetime).Unix(),
"iss": appID,
}
headerJSON, err := json.Marshal(header)
if err != nil {
return "", fmt.Errorf("encoding JWT header: %w", err)
}
claimsJSON, err := json.Marshal(claims)
if err != nil {View on GitHub (pinned to 0ea1f775a7)
Solutions
- Download the private key .pem from the GitHub App's settings page (GitHub generates an RSA key) and point GITHUB_APP_PRIVATE_KEY_PATH at that file
- If a self-generated key must be used, generate an RSA one: openssl genrsa -out app.pem 2048 (PKCS#1) or openssl genpkey -algorithm RSA -pkeyopt rsa_keygen_bits:2048 (PKCS#8)
- Verify the key type before shipping: openssl pkey -in app.pem -noout -text should print an RSA PRIVATE KEY / 'Public-Key: (2048 bit)'
- Check that a PEM mistransformation did not occur (e.g. a JSON/JWK export re-encoded as EC) and re-export the original GitHub-issued PEM
Example fix
// before: self-generated Ed25519 key
// openssl genpkey -algorithm ED25519 -out app.pem
// -> error: private key is ed25519.PrivateKey, want an RSA key
// after: GitHub-issued RSA key, or generated as RSA
// download {base}/settings/apps/{app}/private_key (RSA .pem)
// or: openssl genpkey -algorithm RSA -pkeyopt rsa_keygen_bits:2048 -out app.pem Defensive patterns
Strategy: validation
Validate before calling
func isRSAPEM(pemBytes []byte) bool {
block, _ := pem.Decode(pemBytes)
if block == nil {
return false
}
if _, err := x509.ParsePKCS1PrivateKey(block.Bytes); err == nil {
return true
}
k, err := x509.ParsePKCS8PrivateKey(block.Bytes)
return err == nil && k != nil && strings.Contains(fmt.Sprintf("%T", k), "rsa")
} Type guard
func asRSAKey(anyKey any) (*rsa.PrivateKey, bool) {
k, ok := anyKey.(*rsa.PrivateKey)
return k, ok
} Try / catch
if _, err := githubapp.NewProvider(cfg, logger); err != nil {
if strings.Contains(err.Error(), "want an RSA key") {
// key algorithm wrong: fetch the GitHub-issued .pem
}
} Prevention
- Always use the .pem downloaded from the GitHub App settings page; never generate your own key for a GitHub App
- Add a CI check that runs openssl pkey -in app.pem -noout -text and greps for RSA before deploying
- Validate the key with x509 parsing in a startup probe so the process fails fast instead of at first token refresh
When it happens
Trigger: NewProvider is called with Config.PrivateKeyPEM containing an ECDSA P-256 key generated via 'openssl ecparam -genkey', an Ed25519 key from 'ssh-keygen -t ed25519' converted to PEM, or an openssl 'PRIVATE KEY' block from 'openssl genpkey -algorithm ED25519'. PKCS#1 parse fails, PKCS#8 parse succeeds, the type assertion parsed.(*rsa.PrivateKey) at internal/githubapp/githubapp.go:76 fails, and the %T in the message reports e.g. '*ecdsa.PrivateKey'.
Common situations: A developer generates their own keypair instead of downloading the .pem from the GitHub App settings page; a CI pipeline converts the GitHub-issued key and re-wraps it with the wrong algorithm; an org rotates keys using a generic 'openssl genpkey' template that defaults to a non-RSA algorithm.
Related errors
- invalid GitHub App private key: %w
- failed to unmarshal toolsets: %w
- failed to unmarshal tools: %w
- failed to build inventory: %w
- signing JWT: %w
AI-assisted analysis of github/github-mcp-server@0ea1f775a7 (2026-08-15).
Data as JSON: /api/errors/75f0e853cb48010d.
Report an issue: GitHub.