{"record":{"id":"92e7c6ea5e156a16","repo":"FiloSottile/age","slug":"not-a-plugin-recipient-v","errorCode":null,"errorMessage":"not a plugin recipient: %v","messagePattern":"not a plugin recipient: (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"plugin/encode.go","lineNumber":63,"sourceCode":"// 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) {\n\t\t\treturn false\n\t\t}\n\t}","sourceCodeStart":45,"sourceCodeEnd":81,"githubUrl":"https://github.com/FiloSottile/age/blob/b74dce4cdbe35b5e5f66c06d9612b72f89028758/plugin/encode.go#L45-L81","documentation":"If the string decodes as bech32 but its human-readable part does not start with 'age1', it is not an age/plugin recipient. ParseRecipient rejects it so callers do not misinterpret unrelated bech32 strings (e.g. nostr npub, lightning invoices) as plugin recipients.","triggerScenarios":"ParseRecipient called with a bech32 string like 'npub1...', 'lnbc1...', 'AGE-PLUGIN-...-1...' (identity, not recipient), or 'AGE-SECRET-KEY-...'.","commonSituations":"Wrong variable passed (identity instead of recipient); integrating other bech32 ecosystems and mixing strings; users pasting plugin identity lines where a recipient was requested.","solutions":["Check the string starts with 'age1' before calling ParseRecipient.","If it starts with AGE-PLUGIN- or AGE-SECRET-KEY-, use the identity parser or native identity handling instead.","Ask the user for the recipient (age1...) not the identity.","Route parsing by prefix in dispatch code."],"exampleFix":"// before\nname, data, err := plugin.ParseRecipient(idString)\n// after\nif strings.HasPrefix(idString, \"AGE-\") { /* it's an identity, not a recipient */ }\nname, data, err := plugin.ParseRecipient(\"age1frood1qqq...\")","handlingStrategy":"validation","validationCode":"if !strings.HasPrefix(s, \"age1\") { return fmt.Errorf(\"%q is not an age recipient\", s) }","typeGuard":"func isRecipientString(s string) bool { return strings.HasPrefix(strings.TrimSpace(s), \"age1\") }","tryCatchPattern":"name, data, err := plugin.ParseRecipient(s)\nif err != nil {\n\tif strings.Contains(err.Error(), \"not a plugin recipient\") { return fmt.Errorf(\"expected age1... recipient, got %q\", s) }\n\treturn err\n}","preventionTips":["Distinguish identity vs recipient variables clearly in code","Route parsing by prefix (AGE- vs age1)","Educate users: recipients start with age1, identities with AGE-"],"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"}