larksuite/cli · error
header %q is protected; rerun with allow_protected_header_ed
Error message
header %q is protected; rerun with allow_protected_header_edits
What it means
ensureHeaderEditable rejects edits to headers listed in protectedHeaders unless PatchOptions.AllowProtectedHeaderEdits is true. The library protects structural headers (like Subject handling, MIME headers, or transport headers) from accidental modification because changing them can corrupt the draft's semantics.
Source
Thrown at shortcuts/mail/draft/patch.go:152
}
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{}
for _, addr := range addrs {
if strings.TrimSpace(addr.Address) == "" {
return fmt.Errorf("recipient address is empty")
}
key := strings.ToLower(strings.TrimSpace(addr.Address))
if seen[key] {
continueView on GitHub (pinned to 7fd6ef3c07)
Solutions
- If the edit is intentional, set PatchOptions{AllowProtectedHeaderEdits: true}.
- Use the dedicated ops instead of generic set_header (e.g. set_subject for Subject).
- Check the protectedHeaders set in patch.go to see which names require the flag.
- Restrict AllowProtectedHeaderEdits to trusted admin flows; keep it false by default.
Example fix
// before
err := Apply(ctx, dctx, ops, PatchOptions{})
// after
err := Apply(ctx, dctx, ops, PatchOptions{AllowProtectedHeaderEdits: true}) Defensive patterns
Strategy: validation
Validate before calling
var protectedHeaders = map[string]bool{ /* mirror patch.go set */ }
func needsProtectedFlag(name string) bool {
return protectedHeaders[strings.ToLower(strings.TrimSpace(name))]
} Type guard
func isEditableHeader(name string, opts PatchOptions) bool {
return opts.AllowProtectedHeaderEdits || !protectedHeaders[strings.ToLower(strings.TrimSpace(name))]
} Try / catch
if err := Apply(ctx, dctx, ops, opts); err != nil {
if strings.Contains(err.Error(), "is protected") {
return fmt.Errorf("use dedicated op or set AllowProtectedHeaderEdits=true: %w", err)
}
return err
} Prevention
- Use dedicated ops (e.g. set_subject) instead of generic set_header for known fields.
- Enable AllowProtectedHeaderEdits only in trusted/admin flows.
- Check the protectedHeaders map before writing header-editing code paths.
- Keep the flag false by default and pass it explicitly where needed.
When it happens
Trigger: Calling Apply with a 'set_header' or 'remove_header' op whose Name (case-insensitive, trimmed) is in the protected set, without setting PatchOptions.AllowProtectedHeaderEdits.
Common situations: Trying to override headers such as MIME-Version, Content-Type, or similar structural headers via a generic header op; migrating scripts that previously rewrote these headers directly.
Related errors
- remove_attachment: %w
- replace_inline: %w
- remove_inline: %w
- 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/a7469cce8b866312.
Report an issue: GitHub.