larksuite/cli · error

body part %s not found

Error message

body part %s not found

What it means

The snapshot records a primary part ID for the requested body kind, but findPart cannot locate that part anywhere in the draft's MIME tree. This means the snapshot's part registry (PrimaryTextPartID/PrimaryHTMLPartID) is out of sync with the actual Part tree — a stale or internally inconsistent snapshot. The library refuses to patch a dangling reference rather than patching the wrong node.

Source

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

func bodyPartForKind(snapshot *DraftSnapshot, bodyKind string, allowRewrite bool) (*Part, error) {
	var partID string
	switch strings.ToLower(bodyKind) {
	case "text/plain":
		partID = snapshot.PrimaryTextPartID
	case "text/html":
		partID = snapshot.PrimaryHTMLPartID
	default:
		return nil, fmt.Errorf("unsupported body kind %q", bodyKind)
	}
	if partID == "" {
		if !allowRewrite {
			return nil, fmt.Errorf("draft has no primary %s body part", bodyKind)
		}
		return ensureBodyPart(snapshot, bodyKind)
	}
	part := findPart(snapshot.Body, partID)
	if part == nil {
		return nil, fmt.Errorf("body part %s not found", partID)
	}
	return part, nil
}

func ensureBodyPart(snapshot *DraftSnapshot, bodyKind string) (*Part, error) {
	partRef := primaryBodyRootRef(&snapshot.Body)
	if partRef == nil {
		return nil, fmt.Errorf("draft has no primary body container")
	}
	return ensureBodyPartRef(partRef, bodyKind)
}

func primaryBodyRootRef(root **Part) **Part {
	if root == nil || *root == nil {
		return root
	}
	part := *root
	if strings.EqualFold(part.MediaType, "multipart/mixed") {

View on GitHub (pinned to 7fd6ef3c07)

Solutions

  1. Re-fetch/re-parse the draft to obtain a fresh, consistent DraftSnapshot before patching.
  2. Fix the mutation code so any tree restructuring updates PrimaryTextPartID/PrimaryHTMLPartID (or leaves them intact).
  3. Clear the stale primary ID (set it to "") so the draft goes through the ensureBodyPart path with allowRewrite instead of dangling.

Example fix

// before: stale snapshot reused after body restructure
part, err := bodyPartForKind(staleSnapshot, "text/html", false)
// after: rebuild snapshot from current draft
snapshot, err := parseDraftSnapshot(currentDraft)
part, err := bodyPartForKind(snapshot, "text/html", false)
Defensive patterns

Strategy: validation

Validate before calling

if snapshot.PrimaryHTMLPartID != "" && findPart(snapshot.Body, snapshot.PrimaryHTMLPartID) == nil {
	// stale reference: re-fetch the draft and rebuild the snapshot
	snapshot = reparseDraft(latestDraft)
}

Type guard

func primaryPartResolvable(s *DraftSnapshot, kind string) bool {
	id := s.PrimaryTextPartID
	if strings.EqualFold(kind, "text/html") { id = s.PrimaryHTMLPartID }
	return id == "" || findPart(s.Body, id) != nil
}

Prevention

When it happens

Trigger: Calling replaceBody or appendBody with a snapshot where PrimaryTextPartID or PrimaryHTMLPartID points to a part ID absent from snapshot.Body — e.g. after external mutation of the tree, a partially-failed earlier patch, or a snapshot built from a MIME message where the referenced leaf was dropped.

Common situations: Reusing a DraftSnapshot parsed from an older version of the draft after the body was restructured; custom code that removed/replaced parts without updating primary IDs; bugs in downstream MIME rebuilds that orphan the primary leaf.

Related errors


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