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

  1. Convert to a supported key type: generate ssh-rsa (>= 3072 bits) or ssh-ed25519 keys for use with age.
  2. Add the supported type to recipients explicitly and regenerate unsupported keys.
  3. 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

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


AI-assisted analysis of FiloSottile/age@b74dce4cdb (2026-08-31). Data as JSON: /api/errors/e0b442cd2ecd5aef. Report an issue: GitHub.