{"record":{"id":"537b693b40854cc5","repo":"larksuite/cli","slug":"draft-has-coupled-text-plain-summary-and-text-html","errorCode":null,"errorMessage":"draft has coupled text/plain summary and text/html body; edit them together with set_body","messagePattern":"draft has coupled text/plain summary and text/html body; edit them together with set_body","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"shortcuts/mail/draft/patch.go","lineNumber":250,"sourceCode":"\t\treturn append([]Address{}, snapshot.Cc...), \"Cc\"\n\tcase \"bcc\":\n\t\treturn append([]Address{}, snapshot.Bcc...), \"Bcc\"\n\tdefault:\n\t\treturn nil, \"\"\n\t}\n}\n\nfunc setRecipientField(snapshot *DraftSnapshot, headerName string, addrs []Address) {\n\tif len(addrs) == 0 {\n\t\tremoveHeader(&snapshot.Headers, headerName)\n\t\treturn\n\t}\n\tupsertHeader(&snapshot.Headers, headerName, formatAddressList(addrs))\n}\n\nfunc replaceBody(snapshot *DraftSnapshot, bodyKind, value string, options PatchOptions) error {\n\tif hasCoupledBodySummary(snapshot) {\n\t\treturn fmt.Errorf(\"draft has coupled text/plain summary and text/html body; edit them together with set_body\")\n\t}\n\tpart, err := bodyPartForKind(snapshot, bodyKind, options.RewriteEntireDraft)\n\tif err != nil {\n\t\treturn err\n\t}\n\tpart.Body = []byte(value)\n\tpart.Dirty = true\n\treturn nil\n}\n\nfunc appendBody(snapshot *DraftSnapshot, bodyKind, value string, options PatchOptions) error {\n\tif hasCoupledBodySummary(snapshot) {\n\t\treturn fmt.Errorf(\"draft has coupled text/plain summary and text/html body; edit them together with set_body\")\n\t}\n\tpart, err := bodyPartForKind(snapshot, bodyKind, options.RewriteEntireDraft)\n\tif err != nil {\n\t\treturn err\n\t}","sourceCodeStart":232,"sourceCodeEnd":268,"githubUrl":"https://github.com/larksuite/cli/blob/7fd6ef3c07182257ce776cdc5a614e122d5bd4b3/shortcuts/mail/draft/patch.go#L232-L268","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Use the set_body op with the full HTML content instead of replace_body — it updates the summary and HTML together","Use replace_body only on drafts where text/plain is not a coupled summary (check the draft's structure first)","If you truly need independent part editing, construct the draft with a non-coupled body structure"],"exampleFix":"// before\napplyOp(Op{Op: \"replace_body\", BodyKind: \"text/html\", Value: html})\n// after\napplyOp(Op{Op: \"set_body\", Value: html}) // updates text/plain summary + html together","handlingStrategy":"validation","validationCode":"if draftHasCoupledSummary(snap) {\n    // use set_body instead of replace_body\n}","typeGuard":null,"tryCatchPattern":"if err := applyOp(op); err != nil && strings.Contains(err.Error(), \"coupled text/plain summary\") {\n    // retry with Op{Op: \"set_body\", Value: html}\n}","preventionTips":["Prefer set_body for drafts that may contain HTML + plain-text summary","Inspect the draft's body structure before choosing replace_body vs set_body","Only use replace_body on drafts you know have a single independent body part"],"tags":["email","draft-patch","mime"],"backgroundTag":"coupled-body-edit-forbidden","analyzedSha":"7fd6ef3c07182257ce776cdc5a614e122d5bd4b3","analyzedAt":"2026-09-04T21:17:44.649Z","contentChangedAt":"2026-09-04T21:17:44.649Z","schemaVersion":2},"datasetVersion":"2026-09-12T02:17:10.037Z"}