larksuite/cli · error

selector must be primary

Error message

selector must be primary

What it means

Thrown by PatchOp.Validate() in shortcuts/mail/draft/model.go:298 when a replace_body or append_body op has a selector that is neither empty nor exactly "primary". The selector identifies which body part to modify; only the "primary" body is supported, and empty is treated as "primary".

Source

Thrown at shortcuts/mail/draft/model.go:298

	case "add_recipient", "remove_recipient":
		if !isRecipientField(op.Field) {
			return fmt.Errorf("recipient field must be one of to/cc/bcc")
		}
		if strings.TrimSpace(op.Address) == "" {
			return fmt.Errorf("%s requires address", op.Op)
		}
	case "set_reply_to":
		if len(op.Addresses) == 0 {
			return fmt.Errorf("set_reply_to requires addresses")
		}
	case "clear_reply_to":
	case "set_body", "set_reply_body":
	case "replace_body", "append_body":
		if !isBodyKind(op.BodyKind) {
			return fmt.Errorf("body_kind must be text/plain or text/html")
		}
		if op.Selector != "" && op.Selector != "primary" {
			return fmt.Errorf("selector must be primary")
		}
	case "set_header":
		if strings.TrimSpace(op.Name) == "" {
			return fmt.Errorf("set_header requires name")
		}
		if strings.ContainsAny(op.Name, ":\r\n") {
			return fmt.Errorf("set_header: header name must not contain ':', CR, or LF")
		}
		if strings.ContainsAny(op.Value, "\r\n") {
			return fmt.Errorf("set_header: header value must not contain CR or LF")
		}
	case "remove_header":
		if strings.TrimSpace(op.Name) == "" {
			return fmt.Errorf("remove_header requires name")
		}
	case "add_attachment":
		if strings.TrimSpace(op.Path) == "" {
			return fmt.Errorf("add_attachment requires path")

View on GitHub (pinned to 7fd6ef3c07)

Solutions

  1. Remove the selector field entirely (empty defaults to primary).
  2. Set selector to exactly "primary" if you want to be explicit.
  3. If you intended to target a different MIME part, note only the primary body is supported by this op.

Example fix

// before
{"op":"append_body","body_kind":"text/html","selector":"body","value":"<p>x</p>"}

// after
{"op":"append_body","body_kind":"text/html","value":"<p>x</p>"}
Defensive patterns

Strategy: validation

Validate before calling

if op.Selector != "" && op.Selector != "primary" {
    return fmt.Errorf("selector must be empty or \"primary\", got %q", op.Selector)
}

Prevention

When it happens

Trigger: Ops like {"op":"append_body","body_kind":"text/html","selector":"body"} or selector:"main"/"all"; copying a selector value from a different API; leaving a stale selector from another op type in a shared struct.

Common situations: Guessing selector names instead of using the documented "primary"; reusing a generic patch-builder that sets selector for all ops; thinking an alternative selector targets the alternative/compact body of a message.

Understand the failure class

Background: Invalid enum value errors: "Unknown type", "Invalid scope", "must be one of" — when a string is not on the library's allowed list — this error's family across 23 libraries.

Related errors


AI-assisted analysis of larksuite/cli@7fd6ef3c07 (2026-09-04). Data as JSON: /api/errors/80e08353dca347d6. Report an issue: GitHub.