larksuite/cli · error
part %q is an inline MIME part; use remove_inline
Error message
part %q is an inline MIME part; use remove_inline
What it means
The target part exists but is an inline MIME part (Content-Disposition: inline or a non-empty Content-ID), not a downloadable attachment. remove_attachment refuses to delete inline parts because they are referenced from HTML by Content-ID; deleting them silently would break body rendering. The error directs you to the remove_inline operation instead.
Source
Thrown at shortcuts/mail/draft/patch.go:759
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)
}
if snapshot.Body.PartID == partID {
return fmt.Errorf("cannot remove root MIME part")
}
removed := removePart(snapshot.Body, partID)
if !removed {
return fmt.Errorf("attachment part %q not found", partID)
}
return nil
}
func removePart(parent *Part, targetPartID string) bool {
for idx, child := range parent.Children {
if child == nil {
continue
}
if child.PartID == targetPartID {
parent.Children = append(parent.Children[:idx], parent.Children[idx+1:]...)View on GitHub (pinned to 7fd6ef3c07)
Solutions
- Use the remove_inline operation (with part_id or cid) instead of remove_attachment
- Check the part's Content-Disposition/Content-ID in the draft snapshot to classify it before choosing an op
- Re-list draft parts and select only parts with disposition attachment
Example fix
// before
{"op":"remove_attachment","target":{"part_id":"img-part"}}
// after
{"op":"remove_inline","target":{"cid":"image001@domain"}} Defensive patterns
Strategy: validation
Validate before calling
func isInlinePart(p *Part) bool {
return p != nil && (strings.EqualFold(p.ContentDisposition, "inline") || p.ContentID != "")
}
// choose op: if isInlinePart(part) use remove_inline else remove_attachment Type guard
func isInlinePart(p *Part) bool {
return p != nil && (strings.EqualFold(p.ContentDisposition, "inline") || p.ContentID != "")
} Try / catch
if err := applyOp(snap, op); err != nil && strings.Contains(err.Error(), "inline MIME part") {
op.Name = "remove_inline" // switch operation and retry
} Prevention
- Inspect Content-Disposition and Content-ID before selecting remove_attachment vs remove_inline
- List parts and bucket them into attachment vs inline first
- Never blanket-remove all parts of a multipart/related container
When it happens
Trigger: Calling remove_attachment with the part_id of a part whose ContentDisposition is "inline" or which has a ContentID (cid), typically an embedded image inside multipart/related.
Common situations: Agent picked a cid-referenced embedded image thinking it was an attachment; confusing inline embedded images in multipart/related with regular attachments; applying remove_attachment to all parts of a related container.
Related errors
- MIME nesting too deep (max %d levels)
- attachment part %q not found
- no part with cid %q found
- inline cid is empty
- inline cid %q contains invalid characters (spaces, tabs, ang
AI-assisted analysis of larksuite/cli@7fd6ef3c07 (2026-09-04).
Data as JSON: /api/errors/f1e24b3a0499bbef.
Report an issue: GitHub.