getsops/sops · error
failed to parse input as age-ssh public key: %w
Error message
failed to parse input as age-ssh public key: %w
What it means
The recipient had an ssh- prefix so sops parsed it as an SSH public key via agessh.ParseRecipient; the key is not a supported SSH key type or the string is malformed (e.g. an ssh-ed25519 or rsa key is supported, but certain key formats/curves or a truncated/garbled key fail). This is the age-ssh branch of parseRecipient, used when encrypting sops files to SSH keys.
Source
Thrown at age/keysource.go:514
return parsedRecipient, nil
case strings.HasPrefix(recipient, "age1") && strings.Count(recipient, "1") > 1:
parsedRecipient, err := plugin.NewRecipient(recipient, pluginTerminalUI)
if err != nil {
return nil, fmt.Errorf("failed to parse input as age key from age plugin: %w", err)
}
return parsedRecipient, nil
case strings.HasPrefix(recipient, "age1"):
parsedRecipient, err := age.ParseX25519Recipient(recipient)
if err != nil {
return nil, fmt.Errorf("failed to parse input as Bech32-encoded age public key: %w", err)
}
return parsedRecipient, nil
case strings.HasPrefix(recipient, "ssh-"):
parsedRecipient, err := agessh.ParseRecipient(recipient)
if err != nil {
return nil, fmt.Errorf("failed to parse input as age-ssh public key: %w", err)
}
return parsedRecipient, nil
}
return nil, fmt.Errorf("failed to parse input, unknown recipient type: %q", recipient)
}
// parseIdentities attempts to parse one or more age identities from the provided reader.
// One identity per line.
// Empty lines and lines starting with "#" are ignored.
// If allowMultipleKeysPerLine is true, every non-empty lines is split by words,
// and every word is parsed as an identity.
func parseIdentities(r io.Reader, allowMultipleKeysPerLine bool) (ParsedIdentities, error) {
var identities ParsedIdentities
scanner := bufio.NewScanner(r)
for scanner.Scan() {View on GitHub (pinned to 13442bb981)
Solutions
- Use the contents of the .pub file (single line, starts with ssh-ed25519/ssh-rsa/ecdsa-sha2-...) and paste it fully, one key per recipient entry.
- Confirm the key type is supported by agessh: ed25519, rsa, ecdsa p256/p384/p521; regenerate with ssh-keygen -t ed25519 if using DSA/exotic types.
- Check for missing 'comment' handling isn't the issue — the first two fields must be intact; avoid mangling via shell word-splitting (quote the key).
- If using hardware-backed sk- keys, verify age/sops version supports them or use the vendor's age plugin instead.
- Run `ssh-keygen -lf mykey.pub` to confirm the file is a valid public key before adding to .sops.yaml.
Example fix
# before (.sops.yaml) keys: - ssh-ed25519 AAAAC3Nza # truncated // failed to parse input as age-ssh public key: ... # after cat ~/.ssh/id_ed25519.pub # ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAI... user@host keys: - ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAI...full-key... user@host
Defensive patterns
Strategy: validation
Validate before calling
// shell: verify the SSH key is a supported public key before adding to .sops.yaml
head -n1 mykey.pub | awk '{print $1}' | grep -Eq '^(ssh-ed25519|ssh-rsa|ecdsa-sha2-nistp(256|384|521))$' \
|| { echo "unsupported or malformed ssh key type"; exit 1; }
ssh-keygen -lf mykey.pub >/dev/null || { echo "invalid public key"; exit 1; } Try / catch
rk, err := sopsage.MasterKeyFromRecipient(recipient)
if err != nil && strings.Contains(err.Error(), "age-ssh public key") {
return fmt.Errorf("unsupported/malformed SSH key %q: use ssh-ed25519/rsa/ecdsa .pub content: %w", recipient, err)
} Prevention
- Only paste .pub file contents — never private keys or certificates.
- Prefer ed25519 SSH keys; regenerate unsupported types (DSA, sk- in old age).
- Quote SSH keys when passing through shells to avoid field truncation.
- Validate with ssh-keygen -lf before adding to .sops.yaml.
- One key per keys entry in .sops.yaml; no merged authorized_keys blobs.
When it happens
Trigger: parseRecipient receives a recipient starting with ssh-; agessh.ParseRecipient fails because the key body is corrupt, base64 decoding fails, or the SSH key type is unsupported (e.g. some certificate or exotic curve formats).
Common situations: Pasting a private SSH key instead of the .pub; truncated authorized_keys line; unsupported SSH key algorithm (e.g. sk-ed25519 security keys in older age versions, or DSA keys which age rejects); certificate files (ssh-keygen -CA-issued certs) rather than raw public keys.
Understand the failure class
- Parsing and encoding errors: unexpected token, malformed input — why parsers reject input and how to find the real culprit.
Related errors
- failed to parse input as Bech32-encoded age public key: %w
- failed to parse input as age key from age plugin: %w
- could not create encrypted SSH identity: %w
- malformed SSH identity in %q: %w
- incorrect passphrase
AI-assisted analysis of getsops/sops@13442bb981 (2026-09-01).
Data as JSON: /api/errors/4f125ff915acb07b.
Report an issue: GitHub.