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

  1. Trim the subject with strings.TrimSpace before building the op.
  2. Strip or replace CR/LF: strings.ReplaceAll(strings.ReplaceAll(s,"\r"," "),"\n"," ").
  3. Reject the input in your UI/API layer before constructing the patch.
  4. 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

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


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