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

Thrown by PatchOp.Validate() in shortcuts/mail/draft/model.go:308 when a set_header op's Value contains a carriage return or line feed. A value with CRLF would terminate the header line early and inject additional headers, so it is rejected client-side. Newlines inside header values are never valid here.

Source

Thrown at shortcuts/mail/draft/model.go:308

		}
	case "clear_reply_to":
	case "set_body", "set_reply_body":
	case "replace_body", "append_body":
		if !isBodyKind(op.BodyKind) {
			return fmt.Errorf("body_kind must be text/plain or text/html")
		}
		if op.Selector != "" && op.Selector != "primary" {
			return fmt.Errorf("selector must be primary")
		}
	case "set_header":
		if strings.TrimSpace(op.Name) == "" {
			return fmt.Errorf("set_header requires name")
		}
		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")
		}
	case "remove_header":
		if strings.TrimSpace(op.Name) == "" {
			return fmt.Errorf("remove_header requires name")
		}
	case "add_attachment":
		if strings.TrimSpace(op.Path) == "" {
			return fmt.Errorf("add_attachment requires path")
		}
	case "remove_attachment":
		if !op.Target.hasAnyKey() {
			return fmt.Errorf("remove_attachment requires target with at least one of part_id, cid, or token")
		}
	case "add_inline":
		if strings.TrimSpace(op.Path) == "" {
			return fmt.Errorf("add_inline requires path")
		}
		if strings.TrimSpace(op.CID) == "" {

View on GitHub (pinned to 7fd6ef3c07)

Solutions

  1. Strip newlines: strings.TrimSpace(value) for trailing whitespace, or strings.ReplaceAll(value, "\n", " ") to flatten multi-line values.
  2. Reject the input upstream if a newline-containing value indicates tampering.
  3. If you need multiple headers, emit multiple set_header ops instead of one newline-joined value.

Example fix

// before
PatchOp{Op: "set_header", Name: "X-Note", Value: strings.TrimSpace(fileContent)}
// fileContent may still contain internal \n

// after
v := strings.ReplaceAll(fileContent, "\r", "")
v = strings.ReplaceAll(v, "\n", " ")
PatchOp{Op: "set_header", Name: "X-Note", Value: v}
Defensive patterns

Strategy: validation

Validate before calling

if strings.ContainsAny(value, "\r\n") {
    return fmt.Errorf("header value must not contain newlines")
}

Type guard

func safeHeaderValue(v string) bool {
    return !strings.ContainsAny(v, "\r\n")
}

Prevention

When it happens

Trigger: value containing "\n" or "\r\n", e.g. a multi-line value, a value read from a file without trimming the trailing newline, or untrusted input containing "\r\nBcc: attacker@x.com".

Common situations: Reading header values from files, environment variables, or stdin where a trailing newline remains; multi-line text pasted into a header value; deliberate CRLF injection attempts from user-supplied data.

Related errors


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