FiloSottile/age · error
%s stanza has %d arguments, want %d
Error message
%s stanza has %d arguments, want %d
What it means
This error comes from the age plugin protocol client helper expectStanzaWithNoBody. It fires when a protocol stanza expected to carry exactly `wantArgs` arguments and no body instead carries a different number of space-separated arguments on its first line. The plugin protocol is line/stanza based, so argument counts are strict; a mismatch means the other side emitted a malformed stanza. It is a protocol-conformance failure, not a crypto failure.
Source
Thrown at plugin/plugin.go:590
return s.Args[0] == "yes", nil
}
// fatalInteractf prints the error to stderr and sets the broken flag, so the
// Wrap/Unwrap caller can exit with an error.
func (p *Plugin) fatalInteractf(format string, args ...any) error {
p.broken = true
fmt.Fprintf(p.stderr, format, args...)
return fmt.Errorf(format, args...)
}
func (p *Plugin) fatalf(format string, args ...any) int {
fmt.Fprintf(p.stderr, format, args...)
return 1
}
func expectStanzaWithNoBody(s *format.Stanza, wantArgs int) error {
if len(s.Args) != wantArgs {
return fmt.Errorf("%s stanza has %d arguments, want %d", s.Type, len(s.Args), wantArgs)
}
if len(s.Body) != 0 {
return fmt.Errorf("%s stanza has %d bytes of body, want 0", s.Type, len(s.Body))
}
return nil
}
func expectStanzaWithBody(s *format.Stanza, wantArgs int) error {
if err := expectStanzaWithAnyBody(s, wantArgs); err != nil {
return err
}
if len(s.Body) == 0 {
return fmt.Errorf("%s stanza has 0 bytes of body, want >0", s.Type)
}
return nil
}
func expectStanzaWithAnyBody(s *format.Stanza, wantArgs int) error {View on GitHub (pinned to b74dce4cdb)
Solutions
- Check the plugin binary version and upgrade or downgrade it so it matches the plugin protocol revision this library implements
- Print the raw stanza (s.Type and s.Args) at the failure point to see which argument was added or dropped
- If you wrote the plugin, fix the code that formats the reply so it emits exactly the required arguments with no extras
- If you control the harness, regenerate expected stanzas from the protocol spec
Example fix
// before (plugin side)
fmt.Printf("-> unsupported\n\n") // missing required argument
// after
fmt.Printf("-> unsupported %s\n\n", errReason) Defensive patterns
Strategy: validation
Validate before calling
if len(stanza.Args) != wantArgs {
return fmt.Errorf("plugin sent %s with %d args, want %d", stanza.Type, len(stanza.Args), wantArgs)
} Type guard
func isWellFormedStanza(s *format.Stanza, wantArgs int) bool {
return s != nil && len(s.Args) == wantArgs
} Prevention
- Pin the plugin binary version alongside the age library version
- Log raw stanzas (type + args) before validation in tests
- Keep plugin stdout strictly protocol-only
When it happens
Trigger: RecipientV1 or IdentityV1 received a reply stanza (ok, fail, unsupported, or a recipient/identity-specific stanza) whose Args slice length differs from the expected count; e.g. an 'ok' stanza with trailing arguments, or a 'unsupported' stanza where the plugin added an unexpected extra argument.
Common situations: Using a plugin binary that speaks a different (older/newer) plugin protocol revision than this client expects; a buggy third-party age plugin that appends extra fields; a hand-rolled test harness emitting stanzas with wrong argument counts; whitespace/quoting bugs when the plugin builds its reply line.
Related errors
- %s stanza has %d bytes of body, want 0
- %s stanza has 0 bytes of body, want >0
- expected OK stanza, got %q
- expected ok or fail stanza, got %q
- invalid ssh-rsa recipient block
AI-assisted analysis of FiloSottile/age@b74dce4cdb (2026-08-31).
Data as JSON: /api/errors/5e589868467292b9.
Report an issue: GitHub.