{"record":{"id":"16a5f09cc40e846a","repo":"FiloSottile/age","slug":"expected-ok-or-fail-stanza-got-q","errorCode":null,"errorMessage":"expected ok or fail stanza, got %q","messagePattern":"expected ok or fail stanza, got %q","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"plugin/plugin.go","lineNumber":654,"sourceCode":"\t}\n\treturn expectStanzaWithNoBody(ok, 0)\n}\n\nfunc readOkOrFail(sr *format.StanzaReader) (*format.Stanza, error) {\n\ts, err := sr.ReadStanza()\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"failed to read response stanza: %v\", err)\n\t}\n\tswitch s.Type {\n\tcase \"fail\":\n\t\tif err := expectStanzaWithNoBody(s, 0); err != nil {\n\t\t\treturn nil, fmt.Errorf(\"%v\", err)\n\t\t}\n\t\treturn s, nil\n\tcase \"ok\":\n\t\treturn s, nil\n\tdefault:\n\t\treturn nil, fmt.Errorf(\"expected ok or fail stanza, got %q\", s.Type)\n\t}\n}\n\nfunc expectUnsupported(sr *format.StanzaReader) error {\n\tunsupported, err := sr.ReadStanza()\n\tif err != nil {\n\t\treturn fmt.Errorf(\"failed to read unsupported stanza: %v\", err)\n\t}\n\tif unsupported.Type != \"unsupported\" {\n\t\treturn fmt.Errorf(\"expected unsupported stanza, got %q\", unsupported.Type)\n\t}\n\treturn expectStanzaWithNoBody(unsupported, 0)\n}\n\nfunc (p *Plugin) writeError(args []string, err error) error {\n\ts := &format.Stanza{Type: \"error\", Args: args}\n\ts.Body = []byte(err.Error())\n\tif err := s.Marshal(p.stdout); err != nil {","sourceCodeStart":636,"sourceCodeEnd":672,"githubUrl":"https://github.com/FiloSottile/age/blob/b74dce4cdbe35b5e5f66c06d9612b72f89028758/plugin/plugin.go#L636-L672","documentation":"readOkOrFail accepts only 'ok' or 'fail' as the reply to an interactive command; any other stanza type produces this error naming the unexpected type. The plugin responded with a stanza that does not fit the request/response contract for DisplayMessage, RequestValue, or Confirm.","triggerScenarios":"A plugin replies with a data stanza, 'unsupported', or a custom type where the client demanded 'ok'/'fail'; command sequencing desynchronized so a stale stanza from an earlier exchange is read here.","commonSituations":"Plugin not implementing the interactive-message protocol (responds 'unsupported' to a display-message/command); protocol version skew; plugin echoing input or diagnostics onto stdout that the reader interprets as a stanza.","solutions":["Check which stanza type was received (it is quoted in the error) and map it to the plugin's behavior","Confirm the plugin advertises support for the interactive command being issued","Upgrade the plugin; if it cannot support the command it must reply 'fail', not an arbitrary stanza","Ensure plugin diagnostics go to stderr so they never pollute the stanza stream"],"exampleFix":"// before (plugin side)\nfmt.Printf(\"-> maybe\\n\\n\")\n\n// after\nfmt.Printf(\"-> fail\\n\\n\") // or \"-> ok\\n\\n\"","handlingStrategy":"type-guard","validationCode":null,"typeGuard":"func isOkOrFail(s *format.Stanza) bool {\n    return s != nil && (s.Type == \"ok\" || s.Type == \"fail\")\n}","tryCatchPattern":"s, err := readOkOrFail(sr)\nif err != nil {\n    if strings.HasPrefix(err.Error(), \"expected ok or fail stanza\") {\n        return fmt.Errorf(\"plugin does not support this interactive command: %w\", err)\n    }\n    return err\n}","preventionTips":["Reply 'fail' (not an arbitrary stanza) when a command is unsupported","Never echo user input or logs onto the protocol stdout","Sequence commands strictly one request per one reply"],"tags":["age","plugin-protocol","stanza","unexpected-reply"],"backgroundTag":"plugin-protocol-unexpected-stanza","analyzedSha":"b74dce4cdbe35b5e5f66c06d9612b72f89028758","analyzedAt":"2026-08-31T23:59:31.627Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T05:18:18.240Z"}