{"record":{"id":"6eca17b61b67e272","repo":"hashicorp/terraform","slug":"failed-to-parse-ssh-private-key-s","errorCode":null,"errorMessage":"Failed to parse ssh private key: %s","messagePattern":"Failed to parse ssh private key: (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/communicator/ssh/provisioner.go","lineNumber":436,"sourceCode":"\treturn ssh.PublicKeys(ucertSigner), nil\n}\n\nfunc readPrivateKey(pk string) (ssh.AuthMethod, error) {\n\t// We parse the private key on our own first so that we can\n\t// show a nicer error if the private key has a password.\n\tblock, _ := pem.Decode([]byte(pk))\n\tif block == nil {\n\t\treturn nil, errors.New(\"Failed to read ssh private key: no key found\")\n\t}\n\tif block.Headers[\"Proc-Type\"] == \"4,ENCRYPTED\" {\n\t\treturn nil, errors.New(\n\t\t\t\"Failed to read ssh private key: password protected keys are\\n\" +\n\t\t\t\t\"not supported. Please decrypt the key prior to use.\")\n\t}\n\n\tsigner, err := ssh.ParsePrivateKey([]byte(pk))\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"Failed to parse ssh private key: %s\", err)\n\t}\n\n\treturn ssh.PublicKeys(signer), nil\n}\n\nfunc connectToAgent(connInfo *connectionInfo) (*sshAgent, error) {\n\tif !connInfo.Agent {\n\t\t// No agent configured\n\t\treturn nil, nil\n\t}\n\n\tagent, conn, err := sshagent.New()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\t// connection close is handled over in Communicator\n\treturn &sshAgent{","sourceCodeStart":418,"sourceCodeEnd":454,"githubUrl":"https://github.com/hashicorp/terraform/blob/d32a084675427f5ac3f7d2868578ef8b2c1dc525/internal/communicator/ssh/provisioner.go#L418-L454","documentation":"In readPrivateKey, the private key is first PEM-decoded manually. If PEM decoding succeeds (block is non-nil) and the key is not password-protected (no Proc-Type 4,ENCRYPTED header), ssh.ParsePrivateKey is called. This error fires when ParsePrivateKey still fails despite valid PEM — typically due to an unsupported algorithm, corrupted key body, or a key format the linked x/crypto version cannot parse. Note this path does NOT catch password-protected keys of newer formats (OpenSSH new format) where the Proc-Type header is absent.","triggerScenarios":"Providing a private_key that is valid PEM (decodes successfully) but whose algorithm or internal structure ssh.ParsePrivateKey cannot handle. Triggered by: PKCS#8-wrapped keys unsupported by older x/crypto, keys with unexpected headers, or subtly corrupted key bodies that decode as PEM but fail cryptographic parsing.","commonSituations":"User provides a key generated by a newer ssh-keygen or by OpenSSL in PKCS#8 format that the x/crypto version doesn't support. Key was re-encoded or transformed by a secrets manager. Key uses a cipher or KDF that ParsePrivateKey doesn't recognize. Key is a valid PEM but for a non-SSH purpose (e.g., TLS certificate key) that happens to decode.","solutions":["Regenerate the SSH key with ssh-keygen using a standard format: ssh-keygen -t rsa -b 2048 -m PEM -f keyfile.","If the key is in OpenSSH new format, convert to PEM: ssh-keygen -p -m PEM -f keyfile.","Update golang.org/x/crypto to a version that supports your key's algorithm and format.","Verify the key body is intact — compare against the original file with diff or a checksum.","Ensure no trailing characters, BOM, or encoding issues are present in the key material."],"exampleFix":"# before — key in a format ParsePrivateKey can't handle\nconnection {\n  private_key = var.pkcs8_or_new_format_key\n}\n\n# after — convert to standard PEM format\nssh-keygen -p -m PEM -f ~/.ssh/id_rsa\nconnection {\n  private_key = file(\"~/.ssh/id_rsa\")\n}","handlingStrategy":"validation","validationCode":"// Pre-validate the private key using the same logic as readPrivateKey\nfunc validateReadPrivateKey(pk string) error {\n    block, _ := pem.Decode([]byte(pk))\n    if block == nil {\n        return errors.New(\"no PEM block found in private key\")\n    }\n    if block.Headers[\"Proc-Type\"] == \"4,ENCRYPTED\" {\n        return errors.New(\"password-protected keys are not supported; decrypt the key first\")\n    }\n    _, err := ssh.ParsePrivateKey([]byte(pk))\n    return err\n}","typeGuard":"func isUnencryptedPEMKey(pk string) bool {\n    block, _ := pem.Decode([]byte(pk))\n    if block == nil {\n        return false\n    }\n    return block.Headers[\"Proc-Type\"] != \"4,ENCRYPTED\"\n}","tryCatchPattern":"signer, err := ssh.ParsePrivateKey([]byte(pk))\nif err != nil {\n    return nil, fmt.Errorf(\"cannot parse SSH private key (check format/algorithm): %w\", err)\n}","preventionTips":["Generate keys in PEM format: ssh-keygen -t rsa -m PEM -f keyfile.","Decrypt password-protected keys before use: ssh-keygen -p -f keyfile.","Note: the Proc-Type header check only catches old-format encrypted keys; OpenSSH new-format encrypted keys bypass this check and fail at ParsePrivateKey.","Avoid passing TLS or other non-SSH PEM keys to the SSH connection."],"tags":["ssh","crypto","private-key","pem","key-format"],"backgroundTag":null,"analyzedSha":"d32a084675427f5ac3f7d2868578ef8b2c1dc525","analyzedAt":"2026-08-11T18:43:52.779Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}