router-for-me/CLIProxyAPI · error
private_key pem decode failed
Error message
private_key pem decode failed
What it means
Internal guard in sanitizePrivateKey: after either the original input or the reconstructed PEM from rebuildPEM, a second pem.Decode still returns a nil block (keyutil.go:73-76). Practically this indicates the reconstruction produced text that pem.Decode rejects (e.g. bad header formatting), so the key cannot be trusted.
Source
Thrown at internal/auth/vertex/keyutil.go:76
pk := strings.ReplaceAll(raw, "\r\n", "\n")
pk = strings.ReplaceAll(pk, "\r", "\n")
pk = stripANSIEscape(pk)
pk = strings.ToValidUTF8(pk, "")
pk = strings.TrimSpace(pk)
normalized := pk
if block, _ := pem.Decode([]byte(pk)); block == nil {
// Attempt to reconstruct from the textual payload.
if reconstructed, err := rebuildPEM(pk); err == nil {
normalized = reconstructed
} else {
return "", fmt.Errorf("private_key is not valid pem: %w", err)
}
}
block, _ := pem.Decode([]byte(normalized))
if block == nil {
return "", fmt.Errorf("private_key pem decode failed")
}
rsaBlock, err := ensureRSAPrivateKey(block)
if err != nil {
return "", err
}
return string(pem.EncodeToMemory(rsaBlock)), nil
}
func ensureRSAPrivateKey(block *pem.Block) (*pem.Block, error) {
if block == nil {
return nil, fmt.Errorf("pem block is nil")
}
if block.Type == "RSA PRIVATE KEY" {
if _, err := x509.ParsePKCS1PrivateKey(block.Bytes); err != nil {
return nil, fmt.Errorf("private_key invalid rsa: %w", err)
}View on GitHub (pinned to 78f0c4079e)
Solutions
- Discard the edited key and re-download the original JSON key file from GCP unchanged
- Compare the PEM header/footer lines byte-for-byte with a known-good key (-----BEGIN PRIVATE KEY-----, no extra spaces)
- Run the key through: cat sa.json | jq -r .private_key | openssl rsa -noout -check to validate independently of this code
Example fix
# before ----- BEGIN PRIVATE KEY ----- # hand-edited spaces break pem.Decode # after -----BEGIN PRIVATE KEY-----
Defensive patterns
Strategy: validation
Validate before calling
if block, _ := pem.Decode([]byte(pk)); block == nil {
return fmt.Errorf("private_key is not decodable PEM even after cleanup")
} Prevention
- Never hand-edit PEM header lines
- Run openssl pkey -noout on keys during CI validation
- Keep the GCP-downloaded file byte-identical end to end
When it happens
Trigger: rebuildPEM succeeds in extracting base64 and re-encoding, but the resulting block still fails Go's strict pem.Decode (mismatched header spacing, BOM characters, non-UTF8 residue); extremely rare double-failure where the raw input almost decodes but has structural damage.
Common situations: Keys processed through multiple serialization layers (YAML on/off, JSON escapes, terminal paste with ANSI codes) leaving subtle corruption; editing the PEM headers by hand ('----- BEGIN' with a space).
Related errors
- private_key is not valid pem: %w
- private_key uses unsupported format
- missing pem markers
- service account payload is empty
- service account missing private_key
AI-assisted analysis of router-for-me/CLIProxyAPI@78f0c4079e (2026-08-15).
Data as JSON: /api/errors/71a0ea72abbd6a98.
Report an issue: GitHub.