larksuite/cli · error

draft has coupled text/plain summary and text/html body; edi

Error message

draft has coupled text/plain summary and text/html body; edit them together with set_body

What it means

When a draft's text/plain part is detected as a plain-text summary of the text/html body (a coupled summary+html pair), replaceBody refuses to edit either part in isolation, because editing one would desynchronize it from the other. The draft must be edited with set_body, which updates both parts together.

Source

Thrown at shortcuts/mail/draft/patch.go:250

		return append([]Address{}, snapshot.Cc...), "Cc"
	case "bcc":
		return append([]Address{}, snapshot.Bcc...), "Bcc"
	default:
		return nil, ""
	}
}

func setRecipientField(snapshot *DraftSnapshot, headerName string, addrs []Address) {
	if len(addrs) == 0 {
		removeHeader(&snapshot.Headers, headerName)
		return
	}
	upsertHeader(&snapshot.Headers, headerName, formatAddressList(addrs))
}

func replaceBody(snapshot *DraftSnapshot, bodyKind, value string, options PatchOptions) error {
	if hasCoupledBodySummary(snapshot) {
		return fmt.Errorf("draft has coupled text/plain summary and text/html body; edit them together with set_body")
	}
	part, err := bodyPartForKind(snapshot, bodyKind, options.RewriteEntireDraft)
	if err != nil {
		return err
	}
	part.Body = []byte(value)
	part.Dirty = true
	return nil
}

func appendBody(snapshot *DraftSnapshot, bodyKind, value string, options PatchOptions) error {
	if hasCoupledBodySummary(snapshot) {
		return fmt.Errorf("draft has coupled text/plain summary and text/html body; edit them together with set_body")
	}
	part, err := bodyPartForKind(snapshot, bodyKind, options.RewriteEntireDraft)
	if err != nil {
		return err
	}

View on GitHub (pinned to 7fd6ef3c07)

Solutions

  1. Use the set_body op with the full HTML content instead of replace_body — it updates the summary and HTML together
  2. Use replace_body only on drafts where text/plain is not a coupled summary (check the draft's structure first)
  3. If you truly need independent part editing, construct the draft with a non-coupled body structure

Example fix

// before
applyOp(Op{Op: "replace_body", BodyKind: "text/html", Value: html})
// after
applyOp(Op{Op: "set_body", Value: html}) // updates text/plain summary + html together
Defensive patterns

Strategy: validation

Validate before calling

if draftHasCoupledSummary(snap) {
    // use set_body instead of replace_body
}

Try / catch

if err := applyOp(op); err != nil && strings.Contains(err.Error(), "coupled text/plain summary") {
    // retry with Op{Op: "set_body", Value: html}
}

Prevention

When it happens

Trigger: A replace_body patch op (via applyOp) with any body_kind on a snapshot where hasCoupledBodySummary(snapshot) is true — typically drafts created by the API with both text/plain summary and text/html body.

Common situations: Scripts that worked on text-only drafts then applied replace_body to an HTML draft with a plain-text summary; trying to tweak the text/plain 'summary' directly.

Related errors


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