{"record":{"id":"99940cb09162b4b9","repo":"getsops/sops","slug":"failed-to-parse-input-as-bech32-encoded-age-public","errorCode":null,"errorMessage":"failed to parse input as Bech32-encoded age public key: %w","messagePattern":"failed to parse input as Bech32-encoded age public key: %w","errorType":"error_code","errorClass":null,"httpStatus":null,"severity":"error","filePath":"age/keysource.go","lineNumber":494,"sourceCode":"\t\t\terrs = append(errs, err)\n\t\t} else {\n\t\t\tidentities = append(identities, ids...)\n\t\t\tif len(ids) == 0 {\n\t\t\t\tunusedLocations = append(unusedLocations, location)\n\t\t\t}\n\t\t}\n\t}\n\treturn identities, unusedLocations, errs\n}\n\n// parseRecipient attempts to parse a string containing an encoded age public\n// key or a public ssh key.\nfunc parseRecipient(recipient string) (age.Recipient, error) {\n\tswitch {\n\tcase strings.HasPrefix(recipient, \"age1pq1\"):\n\t\tparsedRecipient, err := age.ParseHybridRecipient(recipient)\n\t\tif err != nil {\n\t\t\treturn nil, fmt.Errorf(\"failed to parse input as Bech32-encoded age public key: %w\", err)\n\t\t}\n\n\t\treturn parsedRecipient, nil\n\tcase strings.HasPrefix(recipient, \"age1\") && strings.Count(recipient, \"1\") > 1:\n\t\tparsedRecipient, err := plugin.NewRecipient(recipient, pluginTerminalUI)\n\t\tif err != nil {\n\t\t\treturn nil, fmt.Errorf(\"failed to parse input as age key from age plugin: %w\", err)\n\t\t}\n\t\treturn parsedRecipient, nil\n\tcase strings.HasPrefix(recipient, \"age1\"):\n\t\tparsedRecipient, err := age.ParseX25519Recipient(recipient)\n\t\tif err != nil {\n\t\t\treturn nil, fmt.Errorf(\"failed to parse input as Bech32-encoded age public key: %w\", err)\n\t\t}\n\n\t\treturn parsedRecipient, nil\n\tcase strings.HasPrefix(recipient, \"ssh-\"):\n\t\tparsedRecipient, err := agessh.ParseRecipient(recipient)","sourceCodeStart":476,"sourceCodeEnd":512,"githubUrl":"https://github.com/getsops/sops/blob/13442bb98183887d7a9ac09ec8ab0564673a59d8/age/keysource.go#L476-L512","documentation":"parseRecipient matched the recipient as a hybrid age recipient (age1pq1 prefix) and called filippo.io/age's ParseHybridRecipient, which rejected the string — bad Bech32 checksum, wrong length, illegal characters, or truncated value. MasterKeyFromRecipient and Encrypt both funnel through parseRecipient, so encrypting/initializing a sops file with this recipient fails.","triggerScenarios":"parseRecipient receives a recipient starting with age1pq1; age.ParseHybridRecipient returns an error for a malformed hybrid recipient string.","commonSituations":"Copy-paste truncated the recipient string; manual typing introduced an 'i' vs 'l' / '0' vs 'o' Bech32 confusion; newline or whitespace included in the recipient config; mixing up a hybrid recipient with a plain X25519 recipient from an older age/sops version.","solutions":["Re-copy the full recipient from `age-keygen -y key.txt` (or the plugin's public-key output) and verify it has the age1pq1 prefix with no whitespace.","Validate the Bech32 string: correct character set (no 1, b, i, o except separators), correct length for the key type.","If the key is actually a plain X25519 key, use its age1... form instead of forcing the hybrid prefix.","Confirm sops and the age library versions support hybrid recipients; upgrade sops if the recipient was generated by a newer age tooling.","Store recipients in .sops.yaml via quotes to prevent shell/YAML mangling."],"exampleFix":"# before\n# .sops.yaml\nage: age1pq1t9h4x...   # truncated on copy\n// failed to parse input as Bech32-encoded age public key: ...\n\n# after\nage-keygen -y key.txt > /tmp/pub\n# .sops.yaml\nage: age1pq1t9h4x...full-recipient...   # pasted completely, no whitespace","handlingStrategy":"validation","validationCode":"// Go/CI pre-check for hybrid recipients\nfunc validHybridRecipient(r string) bool {\n    return strings.HasPrefix(r, \"age1pq1\") &&\n        !strings.ContainsAny(r, \" \\t\\n\\r\\\"'\")\n}\n// plus one-time: `sops --encryption-round-trip` or age round-trip with the key","typeGuard":null,"tryCatchPattern":"rk, err := sopsage.MasterKeyFromRecipient(recipient)\nif err != nil {\n    if strings.Contains(err.Error(), \"Bech32-encoded age public key\") {\n        return fmt.Errorf(\"recipient %q is not a valid bech32 key; re-copy from age-keygen -y\", recipient)\n    }\n    return err\n}","preventionTips":["Copy recipients directly from age-keygen -y output; never retype.","Keep recipients in .sops.yaml under version control, one full line each.","Add CI validation that every recipient parses via MasterKeyFromRecipient before encrypting.","Beware Bech32 lookalikes (1/l, 0/o); validate instead of hand-fixing.","Ensure all collaborators' sops versions support the hybrid format."],"tags":["age","bech32","recipient","parsing"],"backgroundTag":"invalid-age-recipient","analyzedSha":"13442bb98183887d7a9ac09ec8ab0564673a59d8","analyzedAt":"2026-09-01T03:53:00.447Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}