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
- Remove the selector field entirely (empty defaults to primary).
- Set selector to exactly "primary" if you want to be explicit.
- 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
- Omit selector entirely; empty already means primary.
- Only "primary" is supported — do not guess other part names.
- Don't share a generic op builder that sets selector for body ops.
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
- body_kind must be text/plain or text/html
- set_recipients requires non-empty addresses
- %s requires address
- set_reply_to requires addresses
- set_header requires name
AI-assisted analysis of larksuite/cli@7fd6ef3c07 (2026-09-04).
Data as JSON: /api/errors/80e08353dca347d6.
Report an issue: GitHub.