larksuite/cli · error
emlbuilder: content ID contains control character: %q
Error message
emlbuilder: content ID contains control character: %q
What it means
validateCID rejects content IDs containing ASCII control characters (0x00-0x1F and 0x7F), since RFC 2045 Content-ID shares Message-ID syntax where control characters are never valid. It is enforced when adding inline or other MIME parts so a malformed cid cannot corrupt the MIME structure. Wrapped into a typed ValidationError by the mail command layer.
Source
Thrown at shortcuts/mail/emlbuilder/builder.go:196
}
return nil
}
// validateDisplayName rejects display names containing CR or LF, which could
// escape the quoted-string encoding used by mail.Address.String() and inject headers.
func validateDisplayName(name string) error {
if strings.ContainsAny(name, "\r\n") {
return fmt.Errorf("emlbuilder: display name contains CR or LF: %q", name) //nolint:forbidigo // intermediate EML builder error; mail command layer wraps into typed ValidationError.
}
return nil
}
// validateCID rejects content IDs containing ASCII control characters (0x00–0x1F, 0x7F).
// RFC 2045 Content-ID has the same syntax as Message-ID; control characters are never valid.
func validateCID(cid string) error {
for _, r := range cid {
if r < 0x20 || r == 0x7f {
return fmt.Errorf("emlbuilder: content ID contains control character: %q", cid) //nolint:forbidigo // intermediate EML builder error; mail command layer wraps into typed ValidationError.
}
}
return nil
}
// From sets the From header. name may be empty.
func (b Builder) From(name, addr string) Builder {
if b.err != nil {
return b
}
if err := validateDisplayName(name); err != nil {
b.err = err
return b
}
b.from = mail.Address{Name: name, Address: addr}
return b
}
View on GitHub (pinned to 7fd6ef3c07)
Solutions
- Trim whitespace and remove control characters (strings.TrimFunc with unicode.IsControl) before passing the cid.
- Use the returned reference from the attach call (generated cid) instead of a hand-built one.
- Validate cids at input time to match a safe pattern such as <[^\x00-\x1f\x7f]+@.+>.
Example fix
// before part, _ := b.AddInline(data, "image/png", cidFromFile) // cid ends with '\n' // after cid := strings.TrimSpace(cidFromFile) part, _ := b.AddInline(data, "image/png", cid)
Defensive patterns
Strategy: validation
Validate before calling
func safeCID(cid string) bool {
for _, r := range cid {
if r < 0x20 || r == 0x7f { return false }
}
return len(cid) > 0
}
// check before b.AddInline(data, mime, cid) Try / catch
part, err := b.AddInline(img, "image/png", cid)
if err != nil {
var verr *ValidationError
if errors.As(err, &verr) { return fmt.Errorf("bad cid %q: %w", cid, verr) }
return err
} Prevention
- Trim whitespace/newlines from cids read from files or config.
- Prefer cids returned by the builder/attach API over hand-built ones.
- Enforce a cid pattern like ^<[^\x00-\x1f\x7f]+@[^\x00-\x1f\x7f]+>$ at input time.
When it happens
Trigger: Calling AddInline or AddOtherPart with a cid string containing control characters (e.g. trailing '\n' from a config read, or a NUL byte in binary-derived input).
Common situations: Content IDs read from files/lines without trimming the newline; cids generated by concatenating binary or template data; copy-pasted cid values with invisible control characters.
Related errors
- multipart part %s missing boundary
- draft has coupled text/plain summary and text/html body; edi
- draft has both text/plain and text/html body parts, but they
- draft has no unique primary body part; use replace_body with
- draft has no primary %s body part
AI-assisted analysis of larksuite/cli@7fd6ef3c07 (2026-09-04).
Data as JSON: /api/errors/3ecd80927a4edbf4.
Report an issue: GitHub.