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

  1. Ensure the recipient string's plugin name matches the plugin this process was invoked as (age-plugin-<p.name>).
  2. Filter recipient strings by prefix age1<p.name> before the plugin processes them, or better, let age dispatch each recipient to the matching plugin.
  3. Fix the recipient's embedded name by re-encoding with plugin.EncodeRecipient under the correct name.
  4. 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

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


AI-assisted analysis of FiloSottile/age@b74dce4cdb (2026-08-31). Data as JSON: /api/errors/07d55ec3f08c4c62. Report an issue: GitHub.