{"record":{"id":"29615cd2706b8bff","repo":"FiloSottile/age","slug":"received-zero-recipient-stanzas","errorCode":null,"errorMessage":"received zero recipient stanzas","messagePattern":"received zero recipient stanzas","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"plugin/client.go","lineNumber":166,"sourceCode":"\t\t\t\treturn nil, nil, err\n\t\t\t}\n\n\t\t\treturn nil, nil, fmt.Errorf(\"%s\", s.Body)\n\t\tcase \"done\":\n\t\t\tbreak ReadLoop\n\t\tdefault:\n\t\t\tif ok, err := r.ui.handle(r.name, conn, s); err != nil {\n\t\t\t\treturn nil, nil, err\n\t\t\t} else if !ok {\n\t\t\t\tif err := writeStanza(conn, \"unsupported\"); err != nil {\n\t\t\t\t\treturn nil, nil, err\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\tif len(stanzas) == 0 {\n\t\treturn nil, nil, fmt.Errorf(\"received zero recipient stanzas\")\n\t}\n\n\treturn stanzas, labels, nil\n}\n\ntype Identity struct {\n\tname     string\n\tencoding string\n\tui       *ClientUI\n}\n\nvar _ age.Identity = &Identity{}\n\nfunc NewIdentity(s string, ui *ClientUI) (*Identity, error) {\n\tname, _, err := ParseIdentity(s)\n\tif err != nil {\n\t\treturn nil, err\n\t}","sourceCodeStart":148,"sourceCodeEnd":184,"githubUrl":"https://github.com/FiloSottile/age/blob/b74dce4cdbe35b5e5f66c06d9612b72f89028758/plugin/client.go#L148-L184","documentation":"The wrap protocol completed ('done' received) but the plugin returned no recipient-stanza, meaning it produced no wrapped file key. An age file cannot be encrypted without at least one stanza, so the client rejects the result.","triggerScenarios":"Calling WrapWithLabels/Wrap with a plugin recipient whose plugin responds 'ok' to stanzas and finishes with 'done' without ever sending a recipient-stanza, e.g. it decided it cannot wrap for the given recipient but failed to send an error stanza.","commonSituations":"A plugin that silently skips unsupported recipients; a buggy or truncated plugin implementation; a plugin that errored internally but forgot to emit an error stanza; a recipient list the plugin cannot match any identity against.","solutions":["Upgrade or fix the plugin so it always returns at least one recipient-stanza or an explicit 'error' stanza.","Verify the recipient is actually supported by that plugin using its CLI.","Check plugin debug output to see why it produced no stanza.","Add a non-plugin fallback recipient (e.g. X25519) so encryption can succeed without the plugin."],"exampleFix":"// before (plugin side, buggy)\nif !supported(recipient) {\n    // silently skips, then writes done\n}\n// after\nif !supported(recipient) {\n    writeStanza(conn, \"error\", \"recipient not supported by this plugin\")\n    return\n}\nwriteStanza(conn, \"recipient-stanza\", \"0\", st.Type, st.Args...)","handlingStrategy":"validation","validationCode":"// Go: confirm the recipient is supported before relying on plugin-only encryption\nif !strings.HasPrefix(recipient, \"age1yubikey1\") {\n    return fmt.Errorf(\"recipient not supported by the yubikey plugin\")\n}","typeGuard":null,"tryCatchPattern":"if err != nil {\n    if strings.Contains(err.Error(), \"received zero recipient stanzas\") {\n        return fmt.Errorf(\"plugin produced no output for this recipient; check support or add a fallback recipient: %w\", err)\n    }\n    return err\n}","preventionTips":["Include a non-plugin fallback recipient in critical files so decryption is never plugin-dependent.","Verify recipient support with the plugin CLI before programmatic use.","Fix/upgrade plugins that silently skip recipients instead of sending an error stanza.","Log plugin transcripts when debugging empty results."],"tags":["go","age","plugin","protocol","empty-result"],"backgroundTag":"plugin-protocol-violation","analyzedSha":"b74dce4cdbe35b5e5f66c06d9612b72f89028758","analyzedAt":"2026-08-31T23:59:31.627Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T05:18:18.240Z"}