larksuite/cli · error
draft has both text/plain and text/html body parts, but they
Error message
draft has both text/plain and text/html body parts, but they are not a supported summary+html pair
What it means
When a draft has both a text/plain and a text/html primary part, set_body can only replace them together if they form a recognized summary+html pair. If the two parts are not such a supported pair (and the input was valid for the coupled path), set_body gives up rather than guessing which part the value should replace.
Source
Thrown at shortcuts/mail/draft/patch.go:299
//
// This mirrors how normal attachments (independent MIME parts) survive
// body edits — giving consistent mental model: attachments and signature
// are draft-level concerns, not body content.
func setBody(snapshot *DraftSnapshot, value string, options PatchOptions) error {
value = autoPreserveSystemManagedRegions(snapshot, value)
switch {
case snapshot.PrimaryTextPartID != "" && snapshot.PrimaryHTMLPartID == "":
return replaceBody(snapshot, "text/plain", value, options)
case snapshot.PrimaryTextPartID == "" && snapshot.PrimaryHTMLPartID != "":
return replaceBody(snapshot, "text/html", value, options)
case snapshot.PrimaryTextPartID != "" && snapshot.PrimaryHTMLPartID != "":
if err := coupledBodySetBodyInputError(snapshot, value); err != nil {
return err
}
if tryApplyCoupledBodySetBody(snapshot, value) {
return nil
}
return fmt.Errorf("draft has both text/plain and text/html body parts, but they are not a supported summary+html pair")
default:
return fmt.Errorf("draft has no unique primary body part; use replace_body with body_kind")
}
}
// autoPreserveSystemManagedRegions extracts system-managed elements
// (signature block and large attachment card) from the draft's old HTML
// body and injects them into value (before any quote block in value, or
// appended when no quote). Order is [sig][card], matching compose-time
// layout [user][sig][card][quote].
//
// For each element, auto-injection is skipped when value's
// user-authored region (before any quote block in value) already
// contains that element — so users who explicitly reconstruct the body
// with their own signature / card are respected. Elements inside a
// quote block in value belong to the quoted original message and are
// ignored for this check.
//View on GitHub (pinned to 7fd6ef3c07)
Solutions
- Use replace_body with an explicit body_kind (text/plain or text/html) to target one part
- Inspect the draft's MIME structure to confirm which parts exist and their relationship
- Remove or rebuild one of the body parts so the draft has a unique primary body
Example fix
// before
applyOp(Op{Op: "set_body", Value: text}) // draft has unrelated text+html parts
// after
applyOp(Op{Op: "replace_body", BodyKind: "text/html", Value: html}) Defensive patterns
Strategy: validation
Validate before calling
if snap.PrimaryTextPartID != "" && snap.PrimaryHTMLPartID != "" && !isCoupledSummaryPair(snap) {
// use replace_body with explicit body_kind
} Try / catch
if err := applyOp(op); err != nil && strings.Contains(err.Error(), "not a supported summary+html pair") {
// retry with Op{Op: "replace_body", BodyKind: "text/html", ...}
} Prevention
- Inspect the MIME structure of externally created drafts before set_body
- Use replace_body with an explicit body_kind for non-standard multipart bodies
- Normalize drafts to a supported single-body or summary+html shape when possible
When it happens
Trigger: A set_body patch op on a snapshot with both PrimaryTextPartID and PrimaryHTMLPartID set, where the parts are NOT a coupled summary pair (so coupledBodySetBodyInputError passes) and tryApplyCoupledBodySetBody fails to apply the value to both parts.
Common situations: Multipart/alternative drafts authored externally with genuinely different text and HTML content (not a summary); forwarded or quoted drafts with two independent body parts.
Related errors
- draft has coupled text/plain summary and text/html body; edi
- draft has no unique primary body part; use replace_body with
- multipart part %s missing boundary
- remove_attachment: %w
- replace_inline: %w
AI-assisted analysis of larksuite/cli@7fd6ef3c07 (2026-09-04).
Data as JSON: /api/errors/01ba6a82d4e23850.
Report an issue: GitHub.