larksuite/cli · error

unsupported patch op %q

Error message

unsupported patch op %q

What it means

Guard in applyOp: the patch op's Op name matches none of the supported operations, so the patch cannot be applied to the draft snapshot.

Source

Thrown at shortcuts/mail/draft/patch.go:145

			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)
	case "remove_signature":
		return removeSignatureOp(snapshot)
	case "set_calendar":
		return applyCalendarSet(snapshot, op.CalendarICS)
	case "remove_calendar":
		return applyCalendarRemove(snapshot)
	default:
		return fmt.Errorf("unsupported patch op %q", op.Op)
	}
	return nil
}

func ensureHeaderEditable(name string, options PatchOptions) error {
	if protectedHeaders[strings.ToLower(strings.TrimSpace(name))] && !options.AllowProtectedHeaderEdits {
		return fmt.Errorf("header %q is protected; rerun with allow_protected_header_edits", name)
	}
	return nil
}

func setRecipients(snapshot *DraftSnapshot, field string, addrs []Address) error {
	field = strings.ToLower(strings.TrimSpace(field))
	if !isRecipientField(field) {
		return fmt.Errorf("recipient field must be one of to/cc/bcc")
	}
	normalized := make([]Address, 0, len(addrs))
	seen := map[string]bool{}

View on GitHub (pinned to 7fd6ef3c07)

Solutions

  1. Check the op name spelling and casing against the supported list in applyOp (shortcuts/mail/draft/patch.go).
  2. Validate op names against an allowlist before calling Apply.
  3. Upgrade or downgrade so client op schema and library version match.
  4. Log the full op JSON to spot empty or mangled Op fields after deserialization.

Example fix

// before
op := PatchOp{Op: "setHeader", ...}
// after
op := PatchOp{Op: "set_header", ...}
Defensive patterns

Strategy: validation

Validate before calling

var supportedOps = map[string]bool{
  "set_subject": true, "set_recipients": true, "add_recipient": true,
  "append_body": true, "set_header": true, "remove_header": true,
  "remove_attachment": true, "add_inline": true, "replace_inline": true,
  "remove_inline": true, "insert_signature": true, "remove_signature": true,
  "set_calendar": true, "remove_calendar": true,
}
func opSupported(op string) bool { return supportedOps[op] }

Type guard

func knownPatchOp(op PatchOp) bool { return supportedOps[op.Op] }
// filter: for i := range ops { if !knownPatchOp(ops[i]) { return fmt.Errorf("unknown op %q", ops[i].Op) } }

Try / catch

if err := Apply(ctx, dctx, ops, opts); err != nil {
    var ue *unsupportedOpError
    if strings.Contains(err.Error(), "unsupported patch op") {
        return fmt.Errorf("check op name against supported list / version mismatch: %w", err)
    }
    return err
}

Prevention

When it happens

Trigger: Calling Apply with a PatchOp whose Op string is not one of set_subject, set_recipients, add_recipient, append_body, set_header, remove_header, remove_attachment, add_inline, replace_inline, remove_inline, insert_signature, remove_signature, set_calendar, remove_calendar — e.g. a typo like 'setheader' or an op from a newer/older schema version.

Common situations: Typos in hand-written op names; ops deserialized from JSON with different casing or renamed keys; client and library versions out of sync so the client emits an op this build does not know.

Understand the failure class

Background: UnsupportedOperationException and "is not supported" errors: when a library deliberately refuses a call — this error's family across 30 libraries.

Related errors


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