larksuite/cli · error
set_header: header value must not contain CR or LF
Error message
set_header: header value must not contain CR or LF
What it means
applyOp validates that a 'set_header' op's value contains no CR or LF. Unencoded newlines in a header value would inject extra headers, so the library rejects the op.
Source
Thrown at shortcuts/mail/draft/patch.go:97
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) == "" {
if token := strings.TrimSpace(tgt.Token); token != "" {
return removeLargeAttachment(snapshot, token)
}View on GitHub (pinned to 7fd6ef3c07)
Solutions
- Sanitize with strings.TrimSpace or replace newlines before building the op.
- For multi-line data, use RFC 2047 encoded words or folded header syntax produced by an encoding library instead of raw '\n'.
- Reject the input at your API boundary before constructing the patch.
Example fix
// before
op := PatchOp{Op: "set_header", Name: "X-Note", Value: multiLineText}
// after
op := PatchOp{Op: "set_header", Name: "X-Note", Value: strings.ReplaceAll(strings.ReplaceAll(multiLineText, "\r", " "), "\n", " ")} Defensive patterns
Strategy: validation
Validate before calling
func validHeaderValue(v string) bool { return !strings.ContainsAny(v, "\r\n") }
// before op: if !validHeaderValue(value) { sanitize or reject } Type guard
func sanitizeHeaderValue(v string) string {
return strings.TrimSpace(strings.ReplaceAll(strings.ReplaceAll(v, "\r", " "), "\n", " "))
} Try / catch
if err := Apply(ctx, dctx, ops, opts); err != nil {
if strings.Contains(err.Error(), "header value must not contain CR or LF") {
return fmt.Errorf("collapse newlines in header value: %w", err)
}
return err
} Prevention
- Sanitize all values read from files, CSVs, or web forms before header ops.
- Prefer encoded-word folding over raw newlines for long/multi-line values.
- Add unit tests that feed CR/LF-containing values and expect rejection.
- Centralize header-value sanitization in one helper used by all op builders.
When it happens
Trigger: Calling Apply with PatchOp{Op:"set_header"} where Value contains '\r' or '\n', e.g. a multi-line value read from user input or a config file.
Common situations: Setting tracking/reference headers from copied text with line breaks; values imported from JSON or CSV with embedded newlines; template output containing newlines.
Related errors
- set_subject: value must not contain CR or LF
- set_header: header name 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/2859b776bcf9107e.
Report an issue: GitHub.