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

  1. Trim whitespace and remove control characters (strings.TrimFunc with unicode.IsControl) before passing the cid.
  2. Use the returned reference from the attach call (generated cid) instead of a hand-built one.
  3. 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

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


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