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

  1. Use the remove_inline operation (with part_id or cid) instead of remove_attachment
  2. Check the part's Content-Disposition/Content-ID in the draft snapshot to classify it before choosing an op
  3. 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

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


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