larksuite/cli · error
cannot remove root MIME part
Error message
cannot remove root MIME part
What it means
removeInline throws this when the requested partID is the draft body's root MIME part (multipart/mixed or similar). The root container holds all other parts, so deleting it would destroy the message structure and is disallowed.
Source
Thrown at shortcuts/mail/draft/patch.go:742
part.ContentDispositionArg = map[string]string{"filename": fileName}
part.ContentID = finalCID
part.TransferEncoding = "base64"
part.Body = content
part.Dirty = true
syncStructuredPartHeaders(part)
return nil
}
func removeInline(snapshot *DraftSnapshot, partID string) error {
part := findPart(snapshot.Body, partID)
if part == nil {
return fmt.Errorf("inline part %q not found", partID)
}
if !isInlinePart(part) {
return fmt.Errorf("part %q is not an inline MIME part", partID)
}
if snapshot.Body == nil || snapshot.Body.PartID == partID {
return fmt.Errorf("cannot remove root MIME part")
}
if !removePart(snapshot.Body, partID) {
return fmt.Errorf("inline part %q not found", partID)
}
return nil
}
func removeAttachment(snapshot *DraftSnapshot, partID string) error {
if snapshot.Body == nil {
return fmt.Errorf("draft has no MIME body")
}
part := findPart(snapshot.Body, partID)
if part == nil {
return fmt.Errorf("attachment part %q not found", partID)
}
if strings.EqualFold(part.ContentDisposition, "inline") || part.ContentID != "" {
return fmt.Errorf("part %q is an inline MIME part; use remove_inline", partID)
}View on GitHub (pinned to 7fd6ef3c07)
Solutions
- Skip the root part ID (compare against the body's own PartID) when iterating parts
- Remove only leaf inline parts; the root is managed implicitly
- Clear the whole body via the body-level operation instead of removing the root part
Example fix
// before for id in $(all part ids); do --remove-inline part_id=$id; done // after for id in $(leaf inline part ids only); do --remove-inline part_id=$id; done
Defensive patterns
Strategy: validation
Validate before calling
snap, _ := draftGet(draftID)
if snap.Body != nil && snap.Body.PartID == partID {
return errors.New("refusing to remove root MIME part")
} Type guard
func isRootPart(snap *DraftSnapshot, id string) bool {
return snap != nil && snap.Body != nil && snap.Body.PartID == id
} Try / catch
if err := removeInline(...); err != nil {
if strings.Contains(err.Error(), "cannot remove root MIME part") {
// skip this ID in bulk-removal loops
}
} Prevention
- Exclude the body's own PartID from removal candidates
- Iterate only leaf parts collected via tree walk
- Never treat the root multipart node as a removable part
When it happens
Trigger: applyOp -> removeInline where snapshot.Body.PartID == partID (or Body is nil while the ID matches), i.e. the caller passed the root part ID.
Common situations: Iterating all part IDs from a draft get and attempting to remove every one, including the root; assuming the root is a removable inline part.
Related errors
- inline part %q not found
- part %q is not an inline MIME part
- add_attachment requires path
- remove_attachment requires target with at least one of part_
- add_inline requires path
AI-assisted analysis of larksuite/cli@7fd6ef3c07 (2026-09-04).
Data as JSON: /api/errors/cc8c298565f4385f.
Report an issue: GitHub.