{"record":{"id":"e27def6300a73271","repo":"kubernetes/kops","slug":"could-not-parse-private-key-unable-to-decode-pem","errorCode":null,"errorMessage":"could not parse private key (unable to decode PEM)","messagePattern":"could not parse private key \\(unable to decode PEM\\)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"pkg/pki/privatekey.go","lineNumber":190,"sourceCode":"}\n\nfunc (k *PrivateKey) WriteToFile(filename string, perm os.FileMode) error {\n\tf, err := os.OpenFile(filename, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, perm)\n\tif err != nil {\n\t\treturn err\n\t}\n\t_, err = k.WriteTo(f)\n\tif err1 := f.Close(); err == nil {\n\t\terr = err1\n\t}\n\treturn err\n}\n\nfunc parsePEMPrivateKey(pemData []byte) (crypto.Signer, error) {\n\tfor {\n\t\tblock, rest := pem.Decode(pemData)\n\t\tif block == nil {\n\t\t\treturn nil, fmt.Errorf(\"could not parse private key (unable to decode PEM)\")\n\t\t}\n\n\t\tswitch block.Type {\n\t\tcase \"RSA PRIVATE KEY\":\n\t\t\tklog.V(10).Infof(\"Parsing pem block: %q\", block.Type)\n\t\t\treturn x509.ParsePKCS1PrivateKey(block.Bytes)\n\t\tcase \"EC PRIVATE KEY\":\n\t\t\tklog.V(10).Infof(\"Parsing pem block: %q\", block.Type)\n\t\t\treturn x509.ParseECPrivateKey(block.Bytes)\n\t\tcase \"PRIVATE KEY\":\n\t\t\tklog.V(10).Infof(\"Parsing pem block: %q\", block.Type)\n\t\t\tk, err := x509.ParsePKCS8PrivateKey(block.Bytes)\n\t\t\tif err != nil {\n\t\t\t\treturn nil, err\n\t\t\t}\n\t\t\treturn k.(crypto.Signer), nil\n\t\tdefault:\n\t\t\tklog.Infof(\"Ignoring unexpected PEM block: %q\", block.Type)","sourceCodeStart":172,"sourceCodeEnd":208,"githubUrl":"https://github.com/kubernetes/kops/blob/4c8573c808a73d578c5eadc86d410646ea0b0d73/pkg/pki/privatekey.go#L172-L208","documentation":"parsePEMPrivateKey loops pem.Decode over the input looking for an 'RSA PRIVATE KEY', 'EC PRIVATE KEY', or 'PRIVATE KEY' block. If pem.Decode cannot extract any block at all (input is empty, whitespace, non-PEM text, or binary), it returns 'could not parse private key (unable to decode PEM)'.","triggerScenarios":"ParsePEMPrivateKey called with empty/nil data; UnmarshalJSON given a string that is neither PEM nor base64-decodable PEM; passing a certificate, public key, CSR, or plaintext secret instead of a private key.","commonSituations":"Pasting the wrong file (cert vs key) into cluster specs; state store keyset entries truncated or corrupted; base64 conventions differing (URL-safe vs StdEncoding) so the fallback decode also fails; double-quoted/escaped PEM with corrupted newlines.","solutions":["Verify the input starts with '-----BEGIN' and is a PRIVATE KEY block, not a CERTIFICATE block.","Check the file/state store entry is non-empty and not truncated (a common cause is failed writes or partial secret fetches).","If the key is base64, decode with base64.StdEncoding and confirm the result is PEM before passing it.","Regenerate the keypair (e.g. 'kops create keypair <cluster>') if the stored material is lost or corrupt."],"exampleFix":"// before\nkey, err := pki.ParsePEMPrivateKey(certPEM) // cert passed instead of key\n// after\nif !bytes.Contains(keyPEM, []byte(\"-----BEGIN\")) || !bytes.Contains(keyPEM, []byte(\"PRIVATE KEY\")) {\n    return fmt.Errorf(\"input is not a PEM private key\")\n}\nkey, err := pki.ParsePEMPrivateKey(keyPEM)","handlingStrategy":"validation","validationCode":"func looksLikePEMKey(b []byte) bool {\n    s := string(b)\n    return strings.HasPrefix(s, \"-----BEGIN\") && strings.Contains(s, \"PRIVATE KEY-----\")\n}","typeGuard":null,"tryCatchPattern":"key, err := pki.ParsePEMPrivateKey(data)\nif err != nil {\n    if strings.Contains(err.Error(), \"unable to decode PEM\") {\n        // input is not PEM: check for cert vs key, truncation, or base64 mismatch\n    }\n    return err\n}","preventionTips":["Confirm the file is the private key, not the certificate or CSR, before parsing.","Verify non-empty input: ParsePEMPrivateKey returns (nil, nil) for empty data — treat that as an error too.","When base64 is involved, decode with base64.StdEncoding and validate PEM headers first.","Guard state-store reads against truncation and verify round-trip after writes."],"tags":["pki","pem-parsing","private-key"],"backgroundTag":"pem-parse-failed","analyzedSha":"4c8573c808a73d578c5eadc86d410646ea0b0d73","analyzedAt":"2026-09-05T04:13:19.212Z","contentChangedAt":"2026-09-05T04:13:19.212Z","schemaVersion":2},"datasetVersion":"2026-09-12T12:17:11.808Z"}