FiloSottile/age · error
%s plugin: %w
Error message
%s plugin: %w
What it means
This is the plugin error wrapper in plugin.Recipient.WrapWithLabels: when any step of the age-plugin protocol fails, the returned error is prefixed with "<plugin-name> plugin: " so users can tell which plugin failed. It is not itself a failure condition; it decorates the underlying cause (couldn't start plugin, protocol error, plugin-reported failure) from the recipient-v1 wrap interaction.
Source
Thrown at plugin/client.go:74
// String returns the recipient encoding string ("age1name1...") or
// "<identity-based recipient>" if r was created by [Identity.Recipient].
func (r *Recipient) String() string {
if r.identity {
return "<identity-based recipient>"
}
return r.encoding
}
func (r *Recipient) Wrap(fileKey []byte) (stanzas []*age.Stanza, err error) {
stanzas, _, err = r.WrapWithLabels(fileKey)
return
}
func (r *Recipient) WrapWithLabels(fileKey []byte) (stanzas []*age.Stanza, labels []string, err error) {
defer func() {
if err != nil {
err = fmt.Errorf("%s plugin: %w", r.name, err)
}
}()
conn, err := openClientConnection(r.name, "recipient-v1")
if err != nil {
return nil, nil, fmt.Errorf("couldn't start plugin: %w", err)
}
defer conn.Close()
// Phase 1: client sends recipient or identity and file key
addType := "add-recipient"
if r.identity {
addType = "add-identity"
}
if err := writeStanza(conn, addType, r.encoding); err != nil {
return nil, nil, err
}
if _, err := writeGrease(conn); err != nil {View on GitHub (pinned to b74dce4cdb)
Solutions
- Check the wrapped error after the prefix: 'couldn't start plugin' means install/fix the age-plugin-<name> binary on PATH.
- Verify the plugin binary is executable and its version matches the age library/plugin protocol you use.
- Validate the recipient string; re-export it from the source plugin if the encoding is corrupt.
- Run the plugin binary directly with the same recipient to see its native error output and debug its configuration.
- Update both age and the plugin to compatible versions if the protocol handshake fails.
Defensive patterns
Strategy: try-catch
Validate before calling
// Verify the plugin is available before wrapping
name := recipient.Name()
if _, err := exec.LookPath("age-plugin-" + name); err != nil {
return fmt.Errorf("plugin binary age-plugin-%s not found on PATH", name)
} Try / catch
stanzas, err := recipient.Wrap(fileKey)
if err != nil {
// err looks like: "<name> plugin: <cause>"
msg := err.Error()
if i := strings.Index(msg, " plugin: "); i >= 0 {
cause := msg[i+len(" plugin: "):]
log.Printf("plugin failure cause: %s", cause)
}
return err
} Prevention
- Install the matching age-plugin-<name> binary and keep it on PATH.
- Pin compatible versions of age and each plugin.
- Test recipient encodings by running the plugin standalone before production use.
- Always read the text after the '<name> plugin:' prefix; it names the real failure.
When it happens
Trigger: Calling Wrap or WrapWithLabels on a plugin Recipient when: the plugin binary age-plugin-<name> is not found or fails to start; the plugin exits early or speaks a malformed protocol; the plugin reports an error stanza during wrap; any connection or stanza write fails.
Common situations: Plugin not installed or not on PATH; wrong plugin binary version (mismatched plugin protocol); a plugin that crashes on the given recipient encoding; corrupted or unsupported recipient string; container/CI environments missing the plugin binary.
Related errors
- couldn't start plugin: %w
- malformed recipient stanza: unexpected argument count
- malformed recipient stanza: invalid index
- malformed recipient stanza: unexpected index
- repeated labels stanza
AI-assisted analysis of FiloSottile/age@b74dce4cdb (2026-08-31).
Data as JSON: /api/errors/ec7ba0c59e5bb4b3.
Report an issue: GitHub.