FiloSottile/age · error
unsupported plugin name: %q
Error message
unsupported plugin name: %q
What it means
In the recipient-v1 plugin protocol, each requested recipient string is parsed and its plugin name compared against the Plugin's own name. If a parsed recipient belongs to a different plugin, RecipientV1 fails that stanza with 'unsupported plugin name' — the plugin was invoked for name X but was asked to wrap a recipient for name Y.
Source
Thrown at plugin/plugin.go:249
// Unsupported stanzas in uni-directional phases are ignored.
}
}
if len(recipientStrings)+len(identityStrings) == 0 {
return p.fatalf("no recipients or identities provided")
}
if len(fileKeys) == 0 {
return p.fatalf("no file keys provided")
}
var recipients, identities []age.Recipient
for i, s := range recipientStrings {
name, data, err := ParseRecipient(s)
if err != nil {
return p.recipientError(i, err)
}
if name != p.name {
return p.recipientError(i, fmt.Errorf("unsupported plugin name: %q", name))
}
if p.recipient == nil {
return p.recipientError(i, fmt.Errorf("recipient encodings not supported"))
}
r, err := p.recipient(data)
if err != nil {
return p.recipientError(i, err)
}
recipients = append(recipients, r)
}
for i, s := range identityStrings {
name, data, err := ParseIdentity(s)
if err != nil {
return p.identityError(i, err)
}
if name != p.name {
return p.identityError(i, fmt.Errorf("unsupported plugin name: %q", name))
}View on GitHub (pinned to b74dce4cdb)
Solutions
- Ensure the recipient string's plugin name matches the plugin this process was invoked as (age-plugin-<p.name>).
- Filter recipient strings by prefix age1<p.name> before the plugin processes them, or better, let age dispatch each recipient to the matching plugin.
- Fix the recipient's embedded name by re-encoding with plugin.EncodeRecipient under the correct name.
- Check the installed binary's name matches the name passed to plugin.New.
Example fix
// before
// age-plugin-frood invoked with: age1wrongoth1qqq...
// after
if !strings.HasPrefix(recipientStr, "age1"+p.name) { skip / report to user }
name, data, err := plugin.ParseRecipient(recipientStr) Defensive patterns
Strategy: validation
Validate before calling
for _, s := range recipientStrings {
if n, _, err := plugin.ParseRecipient(s); err != nil || n != p.Name() {
// skip or surface: this recipient belongs to another plugin
}
} Type guard
func belongsToPlugin(s, pluginName string) bool {
n, _, err := plugin.ParseRecipient(s)
return err == nil && n == pluginName
} Try / catch
name, data, err := plugin.ParseRecipient(s)
if err != nil { return p.recipientError(i, err) }
if name != p.name { return p.recipientError(i, fmt.Errorf("unsupported plugin name: %q", name)) } Prevention
- Let age dispatch each recipient to its matching plugin instead of mixing in one invocation
- Verify recipients carry the same plugin name as the invoked binary
- Keep plugin binary names in sync with names embedded in recipients
- Surface the offending recipient string in error output
When it happens
Trigger: age invoking age-plugin-<x> with a recipient file or -r argument containing an age1<other-plugin>... recipient; mixed recipient lists in one encryption; case where ParseRecipient extracts a different name than p.name.
Common situations: Users passing multiple -r flags across different plugins; stale config pointing at the wrong plugin binary; a typo in the recipient's plugin name section; symlinked/renamed plugin binaries under the wrong name.
Related errors
- malformed recipient stanza: unexpected argument count
- malformed recipient stanza: invalid index
- malformed recipient stanza: unexpected index
- repeated labels stanza
- %s
AI-assisted analysis of FiloSottile/age@b74dce4cdb (2026-08-31).
Data as JSON: /api/errors/07d55ec3f08c4c62.
Report an issue: GitHub.