larksuite/cli · error
set_header: header name must not contain ':', CR, or LF
Error message
set_header: header name must not contain ':', CR, or LF
What it means
Thrown by PatchOp.Validate() in shortcuts/mail/draft/model.go:305 when a set_header op's Name contains a colon, carriage return, or line feed. Colons would break the "Name: Value" header framing and CRLF enables header injection, so names containing them are rejected before any API call.
Source
Thrown at shortcuts/mail/draft/model.go:305
case "set_reply_to":
if len(op.Addresses) == 0 {
return fmt.Errorf("set_reply_to requires addresses")
}
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) == "" {View on GitHub (pinned to 7fd6ef3c07)
Solutions
- Pass only the header name without the colon; put the value in op.Value.
- Sanitize untrusted input: reject or strip any ':', '\r', '\n' from the name before building the op.
- If the value itself contains newlines, fold it per RFC 5322 or split into multiple headers.
Example fix
// before
PatchOp{Op: "set_header", Name: "X-Trace-Id: " + traceID}
// after
PatchOp{Op: "set_header", Name: "X-Trace-Id", Value: traceID} Defensive patterns
Strategy: validation
Validate before calling
if strings.ContainsAny(name, ":\r\n") {
return fmt.Errorf("invalid header name %q", name)
} Type guard
func safeHeaderName(name string) bool {
if name == "" { return false }
return !strings.ContainsAny(name, ":\r\n")
} Prevention
- Pass the name without the colon — value goes in op.Value.
- Treat header names from user input as untrusted: reject ':', CR, LF outright.
- When parsing raw header lines, split on the first ':' and keep only the left side as the name.
When it happens
Trigger: name:"X-Custom: v" (colon included by mistake), name built by concatenating "Name:" + value, or a name containing "\r\n" from untrusted/user-supplied input attempting header injection.
Common situations: Copying a full header line "X-Foo: bar" from raw email source and using it as the name; interpolating user input into the name field — a classic header-injection vector; parsing headers where the split on ':' didn't happen.
Related errors
- set_header: header value 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/449461a2f33c7f93.
Report an issue: GitHub.