FiloSottile/age · error
unknown SSH recipient type: %q
Error message
unknown SSH recipient type: %q
What it means
After successfully parsing the SSH key, ParseRecipient only supports the types ssh-rsa and ssh-ed25519. Any other key type (e.g. ecdsa-sha2-nistp256, sk-ssh-ed25519@openssh.com) hits the default case and is rejected as an unknown SSH recipient type.
Source
Thrown at agessh/agessh.go:186
sshKey: pk,
theirPublicKey: mpk,
}, nil
}
func ParseRecipient(s string) (age.Recipient, error) {
pubKey, _, _, _, err := ssh.ParseAuthorizedKey([]byte(s))
if err != nil {
return nil, fmt.Errorf("malformed SSH recipient: %q: %v", s, err)
}
var r age.Recipient
switch t := pubKey.Type(); t {
case "ssh-rsa":
r, err = NewRSARecipient(pubKey)
case "ssh-ed25519":
r, err = NewEd25519Recipient(pubKey)
default:
return nil, fmt.Errorf("unknown SSH recipient type: %q", t)
}
if err != nil {
return nil, fmt.Errorf("malformed SSH recipient: %q: %v", s, err)
}
return r, nil
}
func ed25519PublicKeyToCurve25519(pk ed25519.PublicKey) ([]byte, error) {
// See https://blog.filippo.io/using-ed25519-keys-for-encryption and
// https://pkg.go.dev/filippo.io/edwards25519#Point.BytesMontgomery.
p, err := new(edwards25519.Point).SetBytes(pk)
if err != nil {
return nil, err
}
return p.BytesMontgomery(), nil
}
View on GitHub (pinned to b74dce4cdb)
Solutions
- Convert to a supported key type: generate ssh-rsa (>= 3072 bits) or ssh-ed25519 keys for use with age.
- Add the supported type to recipients explicitly and regenerate unsupported keys.
- Alternatively encrypt to the plugin/native age recipient types that the version supports.
Example fix
// before age -r 'ecdsa-sha2-nistp256 AAAA...' // unsupported type // after ssh-keygen -t ed25519 -f id_ed25519 age -r "$(cat id_ed25519.pub)"
Defensive patterns
Strategy: type-guard
Validate before calling
k, _, _, _, err := ssh.ParseAuthorizedKey([]byte(s))
if err == nil {
switch k.Type() {
case "ssh-rsa", "ssh-ed25519":
// supported
default:
return fmt.Errorf("unsupported SSH key type %q; use ssh-rsa or ssh-ed25519", k.Type())
}
} Type guard
func isSupportedSSHType(s string) bool {
k, _, _, _, err := ssh.ParseAuthorizedKey([]byte(s))
if err != nil {
return false
}
return k.Type() == "ssh-rsa" || k.Type() == "ssh-ed25519"
} Try / catch
r, err := agessh.ParseRecipient(s)
if err != nil {
if strings.Contains(err.Error(), "unknown SSH recipient type") {
return errors.New("generate an ssh-rsa or ssh-ed25519 key instead")
}
return err
} Prevention
- Standardize team SSH keys on ssh-ed25519 or ssh-rsa (>=3072 bits).
- Check `ssh-keygen -lf key.pub` key type before using it with age.
- Do not use ECDSA, DSA, or sk-* FIDO keys as age SSH recipients.
When it happens
Trigger: Calling agessh.ParseRecipient with a parsed key whose Type() is neither "ssh-rsa" nor "ssh-ed25519" — e.g. ECDSA, DSA, or security-key (FIDO) variants.
Common situations: Users offering ECDSA or hardware security keys (YubiKey sk-* keys) as age recipients; default ssh-keygen output of older distros that generated ecdsa keys; team configs mixing key types.
Related errors
- malformed SSH recipient: %q: %v
- SSH public key is not an RSA key
- unexpected public key type
- pk does not implement ssh.CryptoPublicKey
- RSA key size is too small
AI-assisted analysis of FiloSottile/age@b74dce4cdb (2026-08-31).
Data as JSON: /api/errors/e0b442cd2ecd5aef.
Report an issue: GitHub.