{"record":{"id":"5ff21e42c5d300e7","repo":"FiloSottile/age","slug":"invalid-recipient-encoding-v","errorCode":null,"errorMessage":"invalid recipient encoding: %v","messagePattern":"invalid recipient encoding: (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"plugin/encode.go","lineNumber":60,"sourceCode":"\treturn name, data, nil\n}\n\n// EncodeRecipient encodes a plugin recipient string for a plugin with the given\n// name. If the name is invalid, it returns an empty string.\nfunc EncodeRecipient(name string, data []byte) string {\n\tif !validPluginName(name) {\n\t\treturn \"\"\n\t}\n\ts, _ := bech32.Encode(\"age1\"+strings.ToLower(name), data)\n\treturn s\n}\n\n// ParseRecipient decodes a plugin recipient string. It returns the plugin name\n// in lowercase and the encoded data.\nfunc ParseRecipient(s string) (name string, data []byte, err error) {\n\thrp, data, err := bech32.Decode(s)\n\tif err != nil {\n\t\treturn \"\", nil, fmt.Errorf(\"invalid recipient encoding: %v\", err)\n\t}\n\tif !strings.HasPrefix(hrp, \"age1\") {\n\t\treturn \"\", nil, fmt.Errorf(\"not a plugin recipient: %v\", err)\n\t}\n\tname = strings.TrimPrefix(hrp, \"age1\")\n\tif !validPluginName(name) {\n\t\treturn \"\", nil, fmt.Errorf(\"invalid plugin name: %q\", name)\n\t}\n\treturn name, data, nil\n}\n\nfunc validPluginName(name string) bool {\n\tif name == \"\" {\n\t\treturn false\n\t}\n\tallowed := \"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789+-._\"\n\tfor _, r := range name {\n\t\tif !strings.ContainsRune(allowed, r) {","sourceCodeStart":42,"sourceCodeEnd":78,"githubUrl":"https://github.com/FiloSottile/age/blob/b74dce4cdbe35b5e5f66c06d9612b72f89028758/plugin/encode.go#L42-L78","documentation":"ParseRecipient first decodes the input with bech32; if bech32 decoding fails (bad charset, checksum, mixed case, or length), it wraps the bech32 error as 'invalid recipient encoding'. The string was not decodable at all, so it cannot be a plugin recipient.","triggerScenarios":"plugin.ParseRecipient (or plugin.NewRecipient / RecipientV1 dispatch) called with a string that fails bech32.Decode: typo, whitespace, missing/extra characters, corrupted checksum, or a non-bech32 string entirely.","commonSituations":"Copying recipient strings with surrounding quotes or trailing newlines; hand-typing long strings; storing recipients in files that got mangled (CRLF, truncation); passing an ssh key or email instead of an age recipient.","solutions":["Trim whitespace and re-copy the exact recipient string; verify its bech32 checksum.","Use plugin.EncodeRecipient to regenerate the string from known data.","Confirm the input is an age/plugin recipient and not another key format (ssh, PGP).","If dispatching in RecipientV1, return a clear error to the CLI caller with the offending string."],"exampleFix":"// before\nname, data, err := plugin.ParseRecipient(strings.TrimSpace(raw))\n// after\ns := strings.TrimSpace(raw)\nif s == \"\" || !strings.HasPrefix(s, \"age1\") { return fmt.Errorf(\"not an age recipient: %q\", s) }\nname, data, err := plugin.ParseRecipient(s)","handlingStrategy":"validation","validationCode":"s := strings.TrimSpace(raw)\nif s == \"\" { return errors.New(\"empty recipient\") }\nif _, _, err := bech32.Decode(s); err != nil { return fmt.Errorf(\"not valid bech32: %w\", err) }","typeGuard":"func isBech32(s string) bool { _, _, err := bech32.Decode(strings.TrimSpace(s)); return err == nil }","tryCatchPattern":"name, data, err := plugin.ParseRecipient(raw)\nif err != nil {\n\treturn fmt.Errorf(\"recipient %q: %w\", raw, err)\n}","preventionTips":["Trim whitespace/newlines from pasted strings","Copy strings verbatim without re-typing","Store recipients in files with LF endings and no quoting"],"tags":["go","bech32","parsing","age-encryption"],"backgroundTag":"invalid-encoding-format","analyzedSha":"b74dce4cdbe35b5e5f66c06d9612b72f89028758","analyzedAt":"2026-08-31T23:59:31.627Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T05:18:18.240Z"}