{"record":{"id":"324c6bb4b1e7da82","repo":"FiloSottile/age","slug":"expected-ok-stanza-got-q","errorCode":null,"errorMessage":"expected OK stanza, got %q","messagePattern":"expected OK stanza, got %q","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"plugin/plugin.go","lineNumber":635,"sourceCode":"\t\treturn p.fatalf(\"%v\", err)\n\t}\n\treturn 3\n}\n\nfunc (p *Plugin) identityError(idx int, err error) int {\n\tif err := p.writeError([]string{\"identity\", fmt.Sprint(idx)}, err); err != nil {\n\t\treturn p.fatalf(\"%v\", err)\n\t}\n\treturn 3\n}\n\nfunc expectOk(sr *format.StanzaReader) error {\n\tok, err := sr.ReadStanza()\n\tif err != nil {\n\t\treturn fmt.Errorf(\"failed to read OK stanza: %v\", err)\n\t}\n\tif ok.Type != \"ok\" {\n\t\treturn fmt.Errorf(\"expected OK stanza, got %q\", ok.Type)\n\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:","sourceCodeStart":617,"sourceCodeEnd":653,"githubUrl":"https://github.com/FiloSottile/age/blob/b74dce4cdbe35b5e5f66c06d9612b72f89028758/plugin/plugin.go#L617-L653","documentation":"The client successfully read a stanza but its Type was not 'ok'. expectOk is used after commands that must be acknowledged with a bare 'ok' stanza (recipient/identity wrap, error reporting). Receiving any other stanza type means the plugin responded out of protocol order or with an unexpected reply.","triggerScenarios":"After writeError sends an 'error' stanza, the plugin replies with something other than 'ok'; a plugin replies 'fail' or 'unsupported' where 'ok' is mandated; the client and plugin disagree on which command is in flight, so the reply is the answer to a previous command.","commonSituations":"Plugin does not implement the full plugin protocol (e.g. no support for the unsupported/error handshake); version mismatch causing off-by-one command sequencing; plugin echoing diagnostics onto stdout that get parsed as a stanza line.","solutions":["Log the received stanza type to see what the plugin actually replied","Confirm the plugin supports the command that was just sent (wrap/unwrap recipient-v1/identity-v1)","Upgrade the plugin to a protocol-conformant version","Ensure the plugin never writes human-readable text to stdout (stdout is protocol-only; use stderr)"],"exampleFix":"// before (plugin side)\nfmt.Printf(\"cannot handle this recipient\\n\\n\") // parsed as a stanza\n\n// after\nfmt.Fprintf(os.Stderr, \"cannot handle this recipient\\n\")\nfmt.Printf(\"-> fail\\n\\n\")","handlingStrategy":"type-guard","validationCode":null,"typeGuard":"func isOkStanza(s *format.Stanza) bool {\n    return s != nil && s.Type == \"ok\"\n}","tryCatchPattern":"if err := expectOk(sr); err != nil {\n    var unexpected *unexpectedStanzaError\n    if errors.As(err, &unexpected) {\n        log.Printf(\"plugin replied %q; check plugin protocol support\", unexpected.Type)\n    }\n    return err\n}","preventionTips":["Confirm plugin supports each command before sending it","Route all plugin diagnostics to stderr, never stdout","Keep plugin and client protocol revisions in sync"],"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"}