FiloSottile/age · error

malformed confirm stanza: invalid NO option encoding

Error message

malformed confirm stanza: invalid NO option encoding

What it means

Same as the YES variant: the confirm stanza's optional second argument (the NO option) failed bech32 decoding. When a confirm stanza supplies two arguments, both must be valid bech32 strings.

Source

Thrown at plugin/client.go:373

			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")
		}
		result := "yes"
		if !choseYes {
			result = "no"
		}
		return true, writeStanza(conn, "ok", result)
	default:
		return false, nil
	}
}

// readStanza calls r.ReadStanza and, if set, invokes WaitTimer in a separate
// goroutine if the call takes longer than 5 seconds.

View on GitHub (pinned to b74dce4cdb)

Solutions

  1. Bech32-encode the NO option the same way as YES: bech32.Encode("", []byte("no"))
  2. Alternatively send a one-argument confirm stanza if there is no NO option
  3. Update the plugin binary to a fixed version

Example fix

// before (plugin side)
args := []string{bech32Yes, "no"}
// after
args := []string{bech32Yes, bech32No}
Defensive patterns

Strategy: validation

Validate before calling

// Plugin-side: encode both options identically
yes, _ := bech32.Encode("", []byte("yes"))
no, _ := bech32.Encode("", []byte("no"))

Try / catch

if err := unwrap(); err != nil && strings.Contains(err.Error(), "invalid NO option encoding") {
    return fmt.Errorf("plugin confirm NO label is not bech32; upgrade plugin")
}

Prevention

When it happens

Trigger: Plugin sends a two-argument "confirm" stanza whose NO option is not valid bech32 (plaintext, bad checksum, invalid characters) during Unwrap.

Common situations: Plugin encodes YES correctly but passes the NO label raw; typos in the encoding helper applied to only one of the two options.

Understand the failure class

Related errors


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