larksuite/cli · error
set_header: header name must not contain ':', CR, or LF
Error message
set_header: header name must not contain ':', CR, or LF
What it means
applyOp validates that a 'set_header' op's header name contains none of ':', CR, or LF. A colon would truncate the header name on the wire and newlines enable header injection, so the name is rejected.
Source
Thrown at shortcuts/mail/draft/patch.go:94
return removeRecipient(snapshot, op.Field, op.Address)
case "set_reply_to":
upsertHeader(&snapshot.Headers, "Reply-To", formatAddressList(op.Addresses))
case "clear_reply_to":
removeHeader(&snapshot.Headers, "Reply-To")
case "set_body":
return setBody(snapshot, op.Value, options)
case "set_reply_body":
return setReplyBody(snapshot, op.Value, options)
case "replace_body":
return replaceBody(snapshot, op.BodyKind, op.Value, options)
case "append_body":
return appendBody(snapshot, op.BodyKind, op.Value, options)
case "set_header":
if err := ensureHeaderEditable(op.Name, options); err != nil {
return err
}
if strings.ContainsAny(op.Name, ":\r\n") {
return fmt.Errorf("set_header: header name must not contain ':', CR, or LF")
}
if strings.ContainsAny(op.Value, "\r\n") {
return fmt.Errorf("set_header: header value must not contain CR or LF")
}
upsertHeader(&snapshot.Headers, op.Name, op.Value)
case "remove_header":
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) == "" {View on GitHub (pinned to 7fd6ef3c07)
Solutions
- Pass only the header name without the colon: op.Name = strings.TrimSpace(parts[0]) after splitting on the first ':'.
- Strip CR/LF from the name before constructing the op.
- Validate header names against the RFC 7230 token charset in your caller before patching.
Example fix
// before
op := PatchOp{Op: "set_header", Name: "X-Custom: value", Value: "v"}
// after
parts := strings.SplitN("X-Custom: value", ":", 2)
op := PatchOp{Op: "set_header", Name: strings.TrimSpace(parts[0]), Value: "v"} Defensive patterns
Strategy: validation
Validate before calling
func validHeaderName(name string) bool {
name = strings.TrimSpace(name)
if name == "" || strings.ContainsAny(name, ":\r\n") { return false }
for _, r := range name {
if r <= 32 || r >= 127 { return false }
}
return true
} Type guard
func splitHeaderLine(line string) (name, value string, ok bool) {
name, value, found := strings.Cut(line, ":")
if !found { return "", "", false }
return strings.TrimSpace(name), strings.TrimSpace(value), true
} Try / catch
if err := Apply(ctx, dctx, ops, opts); err != nil {
if strings.Contains(err.Error(), "header name must not") {
return fmt.Errorf("strip ':' / CR / LF from header name: %w", err)
}
return err
} Prevention
- Never pass full 'Name: value' strings as the header Name; split on the first colon.
- Validate header names against the RFC token charset before patching.
- Normalize names (trim + lowercase for checks) before constructing ops.
- Keep user input out of header names entirely; map user choices to fixed names.
When it happens
Trigger: Calling Apply with PatchOp{Op:"set_header"} where Name includes ':' (e.g. passing the full line 'X-Custom: value' as the name) or any '\r'/'\n'.
Common situations: Parsing 'Name: value' pairs from text and passing the whole pair as the name; splitting header lines incorrectly on spaces instead of ':'; unsanitized user-supplied header names.
Related errors
- set_subject: value must not contain CR or LF
- set_header: header value must not contain CR or LF
- recipient field must be one of to/cc/bcc
- recipient address is empty
- draft main body is text/html and text/plain is only its summ
AI-assisted analysis of larksuite/cli@7fd6ef3c07 (2026-09-04).
Data as JSON: /api/errors/5eb1085dc8d051a8.
Report an issue: GitHub.