{"record":{"id":"cf6c38cba8de6a76","repo":"FiloSottile/age","slug":"s-stanza-has-d-bytes-of-body-want-0","errorCode":null,"errorMessage":"%s stanza has %d bytes of body, want 0","messagePattern":"(.+?) stanza has (.+?) bytes of body, want 0","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"plugin/plugin.go","lineNumber":593,"sourceCode":"// fatalInteractf prints the error to stderr and sets the broken flag, so the\n// Wrap/Unwrap caller can exit with an error.\nfunc (p *Plugin) fatalInteractf(format string, args ...any) error {\n\tp.broken = true\n\tfmt.Fprintf(p.stderr, format, args...)\n\treturn fmt.Errorf(format, args...)\n}\n\nfunc (p *Plugin) fatalf(format string, args ...any) int {\n\tfmt.Fprintf(p.stderr, format, args...)\n\treturn 1\n}\n\nfunc expectStanzaWithNoBody(s *format.Stanza, wantArgs int) error {\n\tif len(s.Args) != wantArgs {\n\t\treturn fmt.Errorf(\"%s stanza has %d arguments, want %d\", s.Type, len(s.Args), wantArgs)\n\t}\n\tif len(s.Body) != 0 {\n\t\treturn fmt.Errorf(\"%s stanza has %d bytes of body, want 0\", s.Type, len(s.Body))\n\t}\n\treturn nil\n}\n\nfunc expectStanzaWithBody(s *format.Stanza, wantArgs int) error {\n\tif err := expectStanzaWithAnyBody(s, wantArgs); err != nil {\n\t\treturn err\n\t}\n\tif len(s.Body) == 0 {\n\t\treturn fmt.Errorf(\"%s stanza has 0 bytes of body, want >0\", s.Type)\n\t}\n\treturn nil\n}\n\nfunc expectStanzaWithAnyBody(s *format.Stanza, wantArgs int) error {\n\tif len(s.Args) != wantArgs {\n\t\treturn fmt.Errorf(\"%s stanza has %d arguments, want %d\", s.Type, len(s.Args), wantArgs)\n\t}","sourceCodeStart":575,"sourceCodeEnd":611,"githubUrl":"https://github.com/FiloSottile/age/blob/b74dce4cdbe35b5e5f66c06d9612b72f89028758/plugin/plugin.go#L575-L611","documentation":"expectStanzaWithNoBody rejects stanzas that were required to be body-less but arrived with payload bytes after the argument line. In the age plugin protocol, control stanzas like 'ok' and 'fail' must have an empty body; extra bytes indicate a malformed reply. This protects the client from silently ignoring unexpected data.","triggerScenarios":"A reply stanza read by RecipientV1, IdentityV1, DisplayMessage, Confirm, expectOk, or readOkOrFail passed the argument-count check but s.Body had length > 0; e.g. an 'ok' stanza followed by data lines before the terminating empty line.","commonSituations":"A plugin that writes a trailing newline/text after an 'ok'; a plugin flushing leftover stdout buffer into a control stanza; protocol-version drift where a newer plugin attaches a reason body to 'fail' or 'ok'.","solutions":["Upgrade or replace the plugin binary so control stanzas carry no body","Log the stanza body bytes at failure to identify what the plugin appended","If you authored the plugin, remove any writes between the stanza line and the blank terminator line","Verify no middle layer (pipe wrapper, logging filter) is injecting bytes into the plugin's stdout"],"exampleFix":"// before (plugin side)\nfmt.Printf(\"-> ok\\nextra explanation text\\n\\n\")\n\n// after\nfmt.Printf(\"-> ok\\n\\n\")","handlingStrategy":"validation","validationCode":"if len(stanza.Body) != 0 {\n    return fmt.Errorf(\"%s must have empty body, got %d bytes\", stanza.Type, len(stanza.Body))\n}","typeGuard":"func isBodyless(s *format.Stanza) bool {\n    return s != nil && len(s.Body) == 0\n}","tryCatchPattern":null,"preventionTips":["Terminate every control stanza with a blank line in plugin code","Never interleave human-readable output with stanzas on stdout","Test the plugin against the reference client's strict parser"],"tags":["age","plugin-protocol","stanza","unexpected-body"],"backgroundTag":"plugin-stanza-unexpected-body","analyzedSha":"b74dce4cdbe35b5e5f66c06d9612b72f89028758","analyzedAt":"2026-08-31T23:59:31.627Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T05:18:18.240Z"}