larksuite/cli · error
remove_attachment: %w
Error message
remove_attachment: %w
What it means
This is a wrapper around a resolveTarget failure during the 'remove_attachment' patch op. resolveTarget could not map the op's Target (part ID or locator) to an existing attachment part in the draft snapshot; the underlying cause is preserved via %w.
Source
Thrown at shortcuts/mail/draft/patch.go:119
if err := ensureHeaderEditable(op.Name, options); err != nil {
return err
}
removeHeader(&snapshot.Headers, op.Name)
case "add_attachment":
return addAttachment(dctx, snapshot, op.Path)
case "remove_attachment":
// Priority: part_id > cid > token. When only token is set, route to
// the large attachment path (updates header + HTML card, no MIME
// part to remove). Otherwise, resolve to a concrete part_id.
tgt := op.Target
if strings.TrimSpace(tgt.PartID) == "" && strings.TrimSpace(tgt.CID) == "" {
if token := strings.TrimSpace(tgt.Token); token != "" {
return removeLargeAttachment(snapshot, token)
}
}
partID, err := resolveTarget(snapshot, tgt)
if err != nil {
return fmt.Errorf("remove_attachment: %w", err)
}
return removeAttachment(snapshot, partID)
case "add_inline":
return addInline(dctx, snapshot, op.Path, op.CID, op.FileName, op.ContentType)
case "replace_inline":
partID, err := resolveTarget(snapshot, op.Target)
if err != nil {
return fmt.Errorf("replace_inline: %w", err)
}
return replaceInline(dctx, snapshot, partID, op.Path, op.CID, op.FileName, op.ContentType)
case "remove_inline":
partID, err := resolveTarget(snapshot, op.Target)
if err != nil {
return fmt.Errorf("remove_inline: %w", err)
}
return removeInline(snapshot, partID)
case "insert_signature":
return insertSignatureOp(snapshot, op)View on GitHub (pinned to 7fd6ef3c07)
Solutions
- Print/check the wrapped cause (errors.Unwrap or %v) to see the exact resolve failure.
- Re-fetch a fresh DraftSnapshot before applying the removal.
- Verify the Target partID exists in snapshot.Parts (or equivalent) before patching.
- For token-based large attachments, ensure Token is set so the large-attachment path is used.
Example fix
// before
ops := []PatchOp{{Op: "remove_attachment", Target: Target{PartID: staleID}}}
// after
snapshot := fetchFreshSnapshot(dctx)
if partExists(snapshot, partID) {
ops := []PatchOp{{Op: "remove_attachment", Target: Target{PartID: partID}}}
} Defensive patterns
Strategy: validation
Validate before calling
func canRemoveAttachment(snap *DraftSnapshot, tgt Target) error {
if strings.TrimSpace(tgt.Token) != "" { return nil } // large-attachment path
_, err := resolveTarget(snap, tgt)
return err
} Type guard
func targetExists(snap *DraftSnapshot, tgt Target) bool {
_, err := resolveTarget(snap, tgt)
return err == nil
} Try / catch
if err := Apply(ctx, dctx, ops, opts); err != nil {
var unwrapped error = err
for errors.Unwrap(unwrapped) != nil { unwrapped = errors.Unwrap(unwrapped) }
if strings.HasPrefix(err.Error(), "remove_attachment:") {
return fmt.Errorf("target unresolvable (%v); refresh snapshot", unwrapped)
}
return err
} Prevention
- Always operate on a freshly fetched snapshot, not a cached one.
- Verify the partID exists in the snapshot before removal ops.
- Make removals idempotent: skip if the part is already absent.
- Prefer token targeting for large attachments so the dedicated path is used.
When it happens
Trigger: Calling Apply with PatchOp{Op:"remove_attachment"} whose Target references a partID that does not exist, was already removed, or whose locator format is invalid. Note: a non-empty Token goes through removeLargeAttachment and never reaches this error.
Common situations: Patching a stale snapshot after another patch already removed the attachment; typo in partID; applying remove_attachment to a draft that never had the attachment.
Related errors
- replace_inline: %w
- remove_inline: %w
- header %q is protected; rerun with allow_protected_header_ed
- recipient field must be one of to/cc/bcc
- recipient address is empty
AI-assisted analysis of larksuite/cli@7fd6ef3c07 (2026-09-04).
Data as JSON: /api/errors/a23f69cf1c297ddf.
Report an issue: GitHub.