FiloSottile/age · error
failed to read unsupported stanza: %v
Error message
failed to read unsupported stanza: %v
What it means
expectUnsupported is used when the client probes whether the plugin supports a command (unsupported-stanza handshake in RecipientV1/IdentityV1). A ReadStanza failure here — EOF, broken pipe, or bad framing — is wrapped with this message. It means the plugin never delivered the 'unsupported' acknowledgment the probe expected.
Source
Thrown at plugin/plugin.go:661
return nil, fmt.Errorf("failed to read response stanza: %v", err)
}
switch s.Type {
case "fail":
if err := expectStanzaWithNoBody(s, 0); err != nil {
return nil, fmt.Errorf("%v", err)
}
return s, nil
case "ok":
return s, nil
default:
return nil, fmt.Errorf("expected ok or fail stanza, got %q", s.Type)
}
}
func expectUnsupported(sr *format.StanzaReader) error {
unsupported, err := sr.ReadStanza()
if err != nil {
return fmt.Errorf("failed to read unsupported stanza: %v", err)
}
if unsupported.Type != "unsupported" {
return fmt.Errorf("expected unsupported stanza, got %q", unsupported.Type)
}
return expectStanzaWithNoBody(unsupported, 0)
}
func (p *Plugin) writeError(args []string, err error) error {
s := &format.Stanza{Type: "error", Args: args}
s.Body = []byte(err.Error())
if err := s.Marshal(p.stdout); err != nil {
return fmt.Errorf("failed to write error stanza: %v", err)
}
if err := expectOk(p.sr); err != nil {
return fmt.Errorf("%v", err)
}
return nil
}View on GitHub (pinned to b74dce4cdb)
Solutions
- Verify the plugin binary runs standalone and does not crash on the probe command
- Check the plugin's stderr output for a panic or early exit
- Confirm the plugin implements the command-unsupported reply of the plugin protocol
- Reinstall/upgrade the plugin and check the AGE_PLUGIN path points to the right binary
Example fix
// before (plugin side) — exits without replying to unknown command
if cmd != "recipient-v1" { os.Exit(1) }
// after
if cmd != "recipient-v1" { fmt.Printf("-> unsupported\n\n"); return } Defensive patterns
Strategy: try-catch
Validate before calling
if _, err := os.Stat(pluginPath); err != nil {
return fmt.Errorf("plugin binary %q not found: %w", pluginPath, err)
}
if err := exec.Command(pluginPath).Run(); err != nil {
return fmt.Errorf("plugin binary not executable: %w", err)
} Try / catch
if err := expectUnsupported(sr); err != nil {
if strings.Contains(err.Error(), "failed to read unsupported stanza") {
return fmt.Errorf("plugin %s crashed during capability probe: %w (stderr: %s)", pluginName, err, stderrBuf.String())
}
return err
} Prevention
- Verify plugin binary exists, is executable, and runs before use
- Ensure the plugin answers unknown commands with 'unsupported' instead of exiting
- Probe plugin capabilities once at startup and cache the result
When it happens
Trigger: During RecipientV1/IdentityV1 negotiation the client reads for an 'unsupported' reply; the plugin exits, crashes, or emits a stanza with invalid framing so ReadStanza errors before any 'unsupported' arrives.
Common situations: Plugin binary missing or failing to exec (though that usually fails earlier, at pipe setup); plugin panics on the probing command; plugin speaks a protocol without the unsupported handshake and just terminates; environment issues like a non-executable or wrong-architecture plugin binary.
Related errors
- failed to read OK stanza: %v
- failed to read response stanza: %v
- failed to read line: %w
- malformed file-key stanza: unexpected arguments count
- malformed file-key stanza: invalid index
AI-assisted analysis of FiloSottile/age@b74dce4cdb (2026-08-31).
Data as JSON: /api/errors/5c5b9268862e62c0.
Report an issue: GitHub.