{"record":{"id":"a23f69cf1c297ddf","repo":"larksuite/cli","slug":"remove-attachment-w","errorCode":null,"errorMessage":"remove_attachment: %w","messagePattern":"remove_attachment: %w","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"shortcuts/mail/draft/patch.go","lineNumber":119,"sourceCode":"\t\tif err := ensureHeaderEditable(op.Name, options); err != nil {\n\t\t\treturn err\n\t\t}\n\t\tremoveHeader(&snapshot.Headers, op.Name)\n\tcase \"add_attachment\":\n\t\treturn addAttachment(dctx, snapshot, op.Path)\n\tcase \"remove_attachment\":\n\t\t// Priority: part_id > cid > token. When only token is set, route to\n\t\t// the large attachment path (updates header + HTML card, no MIME\n\t\t// part to remove). Otherwise, resolve to a concrete part_id.\n\t\ttgt := op.Target\n\t\tif strings.TrimSpace(tgt.PartID) == \"\" && strings.TrimSpace(tgt.CID) == \"\" {\n\t\t\tif token := strings.TrimSpace(tgt.Token); token != \"\" {\n\t\t\t\treturn removeLargeAttachment(snapshot, token)\n\t\t\t}\n\t\t}\n\t\tpartID, err := resolveTarget(snapshot, tgt)\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"remove_attachment: %w\", err)\n\t\t}\n\t\treturn removeAttachment(snapshot, partID)\n\tcase \"add_inline\":\n\t\treturn addInline(dctx, snapshot, op.Path, op.CID, op.FileName, op.ContentType)\n\tcase \"replace_inline\":\n\t\tpartID, err := resolveTarget(snapshot, op.Target)\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"replace_inline: %w\", err)\n\t\t}\n\t\treturn replaceInline(dctx, snapshot, partID, op.Path, op.CID, op.FileName, op.ContentType)\n\tcase \"remove_inline\":\n\t\tpartID, err := resolveTarget(snapshot, op.Target)\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"remove_inline: %w\", err)\n\t\t}\n\t\treturn removeInline(snapshot, partID)\n\tcase \"insert_signature\":\n\t\treturn insertSignatureOp(snapshot, op)","sourceCodeStart":101,"sourceCodeEnd":137,"githubUrl":"https://github.com/larksuite/cli/blob/7fd6ef3c07182257ce776cdc5a614e122d5bd4b3/shortcuts/mail/draft/patch.go#L101-L137","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"// before\nops := []PatchOp{{Op: \"remove_attachment\", Target: Target{PartID: staleID}}}\n// after\nsnapshot := fetchFreshSnapshot(dctx)\nif partExists(snapshot, partID) {\n    ops := []PatchOp{{Op: \"remove_attachment\", Target: Target{PartID: partID}}}\n}","handlingStrategy":"validation","validationCode":"func canRemoveAttachment(snap *DraftSnapshot, tgt Target) error {\n    if strings.TrimSpace(tgt.Token) != \"\" { return nil } // large-attachment path\n    _, err := resolveTarget(snap, tgt)\n    return err\n}","typeGuard":"func targetExists(snap *DraftSnapshot, tgt Target) bool {\n    _, err := resolveTarget(snap, tgt)\n    return err == nil\n}","tryCatchPattern":"if err := Apply(ctx, dctx, ops, opts); err != nil {\n    var unwrapped error = err\n    for errors.Unwrap(unwrapped) != nil { unwrapped = errors.Unwrap(unwrapped) }\n    if strings.HasPrefix(err.Error(), \"remove_attachment:\") {\n        return fmt.Errorf(\"target unresolvable (%v); refresh snapshot\", unwrapped)\n    }\n    return err\n}","preventionTips":["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."],"tags":["email","draft-patch","invalid-target"],"backgroundTag":"invalid-target-reference","analyzedSha":"7fd6ef3c07182257ce776cdc5a614e122d5bd4b3","analyzedAt":"2026-09-04T21:17:44.649Z","contentChangedAt":"2026-09-04T21:17:44.649Z","schemaVersion":2},"datasetVersion":"2026-09-12T02:17:10.037Z"}