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

  1. 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.
  2. 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.
  3. 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).
  4. If using hardware-backed sk- keys, verify age/sops version supports them or use the vendor's age plugin instead.
  5. 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

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

Related errors


AI-assisted analysis of getsops/sops@13442bb981 (2026-09-01). Data as JSON: /api/errors/4f125ff915acb07b. Report an issue: GitHub.