larksuite/cli · error
set_subject: value must not contain CR or LF
Error message
set_subject: value must not contain CR or LF
What it means
applyOp validates that the value supplied for a 'set_subject' patch op contains no CR or LF characters. Header injection via newlines would split or forge additional headers in the outgoing draft, so the library rejects it outright.
Source
Thrown at shortcuts/mail/draft/patch.go:68
for _, op := range patch.Ops {
if err := applyOp(dctx, snapshot, op, patch.Options); err != nil {
return err
}
if bodyChangingOps[op.Op] {
hasBodyChange = true
}
}
if err := postProcessInlineImages(dctx, snapshot, hasBodyChange); err != nil {
return err
}
return refreshSnapshot(snapshot)
}
func applyOp(dctx *DraftCtx, snapshot *DraftSnapshot, op PatchOp, options PatchOptions) error {
switch op.Op {
case "set_subject":
if strings.ContainsAny(op.Value, "\r\n") {
return fmt.Errorf("set_subject: value must not contain CR or LF")
}
upsertHeader(&snapshot.Headers, "Subject", op.Value)
case "set_recipients":
return setRecipients(snapshot, op.Field, op.Addresses)
case "add_recipient":
return addRecipient(snapshot, op.Field, Address{Name: op.Name, Address: op.Address})
case "remove_recipient":
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)View on GitHub (pinned to 7fd6ef3c07)
Solutions
- Trim the subject with strings.TrimSpace before building the op.
- Strip or replace CR/LF: strings.ReplaceAll(strings.ReplaceAll(s,"\r"," "),"\n"," ").
- Reject the input in your UI/API layer before constructing the patch.
- If a multi-line subject is truly needed, fold it with proper RFC 2047 encoding rather than raw newlines.
Example fix
// before
ops := []PatchOp{{Op: "set_subject", Value: rawSubject}}
// after
subject := strings.TrimSpace(strings.ReplaceAll(strings.ReplaceAll(rawSubject, "\r", " "), "\n", " "))
ops := []PatchOp{{Op: "set_subject", Value: subject}} Defensive patterns
Strategy: validation
Validate before calling
func validSubject(s string) bool { return !strings.ContainsAny(s, "\r\n") }
// before op: if !validSubject(subject) { return fmt.Errorf("subject contains newline") } Type guard
func safeHeaderValue(s string) (string, bool) {
s = strings.TrimSpace(s)
return s, !strings.ContainsAny(s, "\r\n")
} Try / catch
if err := Apply(ctx, dctx, ops, opts); err != nil {
if strings.Contains(err.Error(), "CR or LF") {
return fmt.Errorf("subject/value contains newline; sanitize input: %w", err)
}
return err
} Prevention
- TrimSpace all header-derived values before building PatchOps.
- Sanitize user-supplied subjects at the UI/API boundary.
- Treat \r and \n as forbidden characters in any header value.
- Use encoding libraries for any multi-line header data.
When it happens
Trigger: Calling Apply with a PatchOp{Op:"set_subject"} whose Value contains '\r' or '\n' (e.g. a subject pasted from a multi-line source or built from unsanitized user input).
Common situations: Copying an email subject from a text file or web form with trailing newline; user input injected into the subject field; CSV/JSON imports with embedded line breaks.
Related errors
- set_header: header name 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/3e89018c3b60abd5.
Report an issue: GitHub.