larksuite/cli · error

emlbuilder: at least one recipient (To/CC/BCC) is required

Error message

emlbuilder: at least one recipient (To/CC/BCC) is required

What it means

Build() requires at least one recipient among To, CC, and BCC unless the builder was configured with AllowNoRecipients. An EML with no recipient headers is not deliverable, so the builder refuses to serialize it by default. Wrapped into a typed ValidationError by the mail command layer.

Source

Thrown at shortcuts/mail/emlbuilder/builder.go:682

}

// Build validates the builder and returns the raw EML bytes.
//
// Constraints (Lark API requirements):
//   - From is mandatory.
//   - At least one of To/CC/BCC must be set.
//   - Line endings are LF (\n), not CRLF.
//   - Content-Type parameters are written on a single line (no header folding).
//   - Non-ASCII body content is base64 (StdEncoding) encoded.
func (b Builder) Build() ([]byte, error) {
	if b.err != nil {
		return nil, b.err
	}
	if b.from.Address == "" {
		return nil, fmt.Errorf("emlbuilder: From address is required") //nolint:forbidigo // intermediate EML builder error; mail command layer wraps into typed ValidationError.
	}
	if !b.allowNoRecipients && len(b.to)+len(b.cc)+len(b.bcc) == 0 {
		return nil, fmt.Errorf("emlbuilder: at least one recipient (To/CC/BCC) is required") //nolint:forbidigo // intermediate EML builder error; mail command layer wraps into typed ValidationError.
	}

	date := b.date
	if date.IsZero() {
		date = time.Now()
	}

	msgID := b.messageID
	if msgID == "" {
		msgID = fmt.Sprintf("%d.%d@larksuite-cli", date.UnixNano(), rand.Int63())
	}

	var buf bytes.Buffer

	// ── Top-level headers ──────────────────────────────────────────────────────
	// Order follows common convention; Lark API does not require a specific order.
	writeHeader(&buf, "Subject", encodeHeaderValue(b.subject))
	writeHeader(&buf, "From", b.from.String())

View on GitHub (pinned to 7fd6ef3c07)

Solutions

  1. Add at least one recipient with b.To(...), b.CC(...), or b.BCC(...) before Build().
  2. If the message genuinely needs no recipients (e.g. draft/template emission), enable the builder's allow-no-recipients option.
  3. Validate the recipient list is non-empty at the application level before invoking the builder to give a clearer message.

Example fix

// before
b.From("me@x.com")
raw, err := b.Build() // error: at least one recipient required
// after
b.From("me@x.com")
b.To("dest@x.com")
raw, err := b.Build()
Defensive patterns

Strategy: validation

Validate before calling

if len(recipients) == 0 {
    return errors.New("no recipients after filtering; refusing to build EML")
}
for _, r := range recipients { b.To(r) }

Try / catch

raw, err := b.Build()
if err != nil {
    var verr *ValidationError
    if errors.As(err, &verr) && strings.Contains(err.Error(), "at least one recipient") {
        return fmt.Errorf("recipient list empty: %w", verr)
    }
    return err
}

Prevention

When it happens

Trigger: Calling Build() with no To/CC/BCC added and allowNoRecipients false — e.g. all recipient lists empty because the recipients parameter was an empty slice or every address was filtered out.

Common situations: Recipient list filtered to zero entries (e.g. by dedupe or domain rules) before building; config-driven recipient list missing/blank; batch logic skipping the only recipient due to a validation rule.

Understand the failure class

Background: "missing required argument" and "the following required arguments were not provided": what required-argument errors mean and how to fix them — this error's family across 20 libraries.

Related errors


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