FiloSottile/age · error

malformed confirm stanza: unexpected number of arguments

Error message

malformed confirm stanza: unexpected number of arguments

What it means

In the client's plugin UI handler, a "confirm" stanza from the plugin must carry exactly one or two arguments (the YES option and optionally the NO option, bech32-encoded). A different argument count is rejected as malformed per the age-plugin protocol spec.

Solutions

  1. Fix the plugin to send the confirm stanza with exactly one argument (YES) or two (YES and NO), each bech32-encoded
  2. Update the plugin to a spec-conforming release
  3. Inspect the plugin's stanza output (enable debugging or run it manually) to see the malformed argument list

Example fix

// before (plugin side)
writeStanzaWithBody(w, "confirm", "yes", "no", "maybe", message)
// after
writeStanzaWithBody(w, "confirm", bech32Encode("yes"), bech32Encode("no"), message)
Defensive patterns

Strategy: validation

Validate before calling

// Plugin-side pre-check before emitting confirm
if len(args) < 1 || len(args) > 2 { return errors.New("confirm needs 1 or 2 bech32 args") }

Try / catch

if err := unwrapWithPlugin(); err != nil && strings.Contains(err.Error(), "malformed confirm stanza") {
    return fmt.Errorf("plugin %s has a broken confirm implementation; upgrade it", pluginName)
}

Prevention

When it happens

Trigger: A plugin calls ui.Confirm with its protocol message built incorrectly — e.g. sending a confirm stanza with zero arguments or three or more — while the client's Unwrap is dispatching stanzas to handle().

Common situations: Custom plugin authors passing empty/extra arguments when emitting the confirm stanza; plugin versions incompatible with the client's protocol implementation.

Understand the failure class

Related errors


AI-assisted analysis of FiloSottile/age@b74dce4cdb (2026-08-31). Data as JSON: /api/errors/4910903a8b1b66cb. Report an issue: GitHub.

Appendix: source

Thrown at plugin/client.go:360

		if c.DisplayMessage == nil {
			return true, writeStanza(conn, "fail")
		}
		if err := c.DisplayMessage(name, string(s.Body)); err != nil {
			return true, writeStanza(conn, "fail")
		}
		return true, writeStanza(conn, "ok")
	case "request-secret", "request-public":
		if c.RequestValue == nil {
			return true, writeStanza(conn, "fail")
		}
		secret, err := c.RequestValue(name, string(s.Body), s.Type == "request-secret")
		if err != nil {
			return true, writeStanza(conn, "fail")
		}
		return true, writeStanzaWithBody(conn, "ok", []byte(secret))
	case "confirm":
		if len(s.Args) != 1 && len(s.Args) != 2 {
			return true, fmt.Errorf("malformed confirm stanza: unexpected number of arguments")
		}
		if c.Confirm == nil {
			return true, writeStanza(conn, "fail")
		}
		yes, err := format.DecodeString(s.Args[0])
		if err != nil {
			return true, fmt.Errorf("malformed confirm stanza: invalid YES option encoding")
		}
		var no []byte
		if len(s.Args) == 2 {
			no, err = format.DecodeString(s.Args[1])
			if err != nil {
				return true, fmt.Errorf("malformed confirm stanza: invalid NO option encoding")
			}
		}
		choseYes, err := c.Confirm(name, string(s.Body), string(yes), string(no))
		if err != nil {
			return true, writeStanza(conn, "fail")

View on GitHub (pinned to b74dce4cdb)