{"record":{"id":"da9f6cd816fa8811","repo":"getsops/sops","slug":"malformed-ssh-identity-in-q-w","errorCode":null,"errorMessage":"malformed SSH identity in %q: %w","messagePattern":"malformed SSH identity in %q: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"age/ssh_parse.go","lineNumber":81,"sourceCode":"\t\t\tif err != nil {\n\t\t\t\treturn nil, err\n\t\t\t}\n\t\t}\n\t\tpassphrasePrompt := func() ([]byte, error) {\n\t\t\tpass, err := pluginTerminalUI.RequestValue(\"\", fmt.Sprintf(\"Enter passphrase for %q:\", keyPath), true)\n\t\t\tif err != nil {\n\t\t\t\treturn nil, fmt.Errorf(\"could not read passphrase for %q: %v\", keyPath, err)\n\t\t\t}\n\t\t\treturn []byte(pass), nil\n\t\t}\n\t\ti, err := agessh.NewEncryptedSSHIdentity(pubKey, contents, passphrasePrompt)\n\t\tif err != nil {\n\t\t\treturn nil, fmt.Errorf(\"could not create encrypted SSH identity: %w\", err)\n\t\t}\n\t\treturn i, nil\n\t}\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"malformed SSH identity in %q: %w\", keyPath, err)\n\t}\n\treturn id, nil\n}\n","sourceCodeStart":63,"sourceCodeEnd":85,"githubUrl":"https://github.com/getsops/sops/blob/13442bb98183887d7a9ac09ec8ab0564673a59d8/age/ssh_parse.go#L63-L85","documentation":"sops parses the SSH private key file with an SSH decoder to obtain its public key. This error means the file could not be parsed as a valid SSH private key at all, so no identity could be derived from it.","triggerScenarios":"parseSSHIdentityFromPrivateKeyFile reads keyPath, and the SSH decoder (x/ssh.ParseRawPrivateKey family) returns an error on the file contents — the file is not a valid PEM/OpenSSH private key, is encrypted in a way the parser rejects without a passphrase, or is truncated.","commonSituations":"Pointing SOPS_AGE_SSH_PRIVATE_KEY_FILE at a public key file, a config file, or a path that doesn't exist; file contains an old PEM-encrypted RSA key; corrupted download; wrong file (e.g. known_hosts) passed in.","solutions":["Verify the path points to a valid SSH PRIVATE key (not .pub): run ssh-keygen -y -f <path>","Check the file exists and is readable: ls -l / cat the file","Decrypt/convert legacy PEM keys: ssh-keygen -p -f <keyfile> to re-save in the new OpenSSH format","Regenerate the key if the file is corrupted: ssh-keygen -t ed25519"],"exampleFix":"// before\nexport SOPS_AGE_SSH_PRIVATE_KEY_FILE=~/.ssh/id_ed25519.pub  # public key, unparseable\n// after\nexport SOPS_AGE_SSH_PRIVATE_KEY_FILE=~/.ssh/id_ed25519       # private key file","handlingStrategy":"validation","validationCode":"info, err := os.Stat(keyPath)\nif err != nil || info.IsDir() { return fmt.Errorf(\"SSH key path missing: %s\", keyPath) }\nhead, _ := os.ReadFile(keyPath)\nif !strings.Contains(string(head), \"PRIVATE KEY\") {\n    return fmt.Errorf(\"%s does not look like a private key\", keyPath)\n}\nif err := exec.Command(\"ssh-keygen\", \"-y\", \"-f\", keyPath).Run(); err != nil {\n    return fmt.Errorf(\"unparseable SSH private key: %w\", err)\n}","typeGuard":"func isSSHPrivateKeyFile(path string) bool {\n    b, err := os.ReadFile(path)\n    return err == nil && strings.Contains(string(b), \"PRIVATE KEY\")\n}","tryCatchPattern":"id, err := parseSSHIdentityFromPrivateKeyFile(keyPath)\nif err != nil {\n    if strings.Contains(err.Error(), \"malformed SSH identity\") {\n        return fmt.Errorf(\"check SOPS_AGE_SSH_PRIVATE_KEY_FILE points to a valid private key: %w\", err)\n    }\n    return err\n}","preventionTips":["Never point config at .pub files","Run ssh-keygen -y -f <key> as a preflight check","Migrate legacy PEM keys to the new OpenSSH format"],"tags":["age","ssh","key-parsing","invalid-key-file"],"backgroundTag":"invalid-private-key-file","analyzedSha":"13442bb98183887d7a9ac09ec8ab0564673a59d8","analyzedAt":"2026-09-01T03:53:00.447Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}