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
- Strip newlines: strings.TrimSpace(value) for trailing whitespace, or strings.ReplaceAll(value, "\n", " ") to flatten multi-line values.
- Reject the input upstream if a newline-containing value indicates tampering.
- 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
- Trim trailing newlines when reading values from files/env/stdin.
- Flatten multi-line values by replacing \r and \n with spaces before setting.
- Emit multiple set_header ops instead of newline-joined values; reject newline-bearing untrusted input as injection.
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
- set_header: header name must not contain ':', CR, or LF
- set_header requires name
- remove_header requires name
- set_recipients requires non-empty addresses
- %s requires address
AI-assisted analysis of larksuite/cli@7fd6ef3c07 (2026-09-04).
Data as JSON: /api/errors/8b34a503c0be3e4f.
Report an issue: GitHub.