Billionmail/BillionMail · error

no recipients specified

Error message

no recipients specified

What it means

Send() validates its input before any network work: if recipients is empty or nil it returns this error immediately. It guards against attempting an SMTP transaction with no RCPT TO, which would fail downstream anyway. This is purely a caller-input problem, not a server issue.

Source

Thrown at core/internal/service/mail_service/sending.go:309

// GenerateMessageID generates a unique Message-ID for email
func (e *EmailSender) GenerateMessageID() string {
	randomBytes := grand.B(16)
	randomID := hex.EncodeToString(randomBytes)
	timestampMillis := time.Now().UnixMilli()

	domain := strings.SplitN(e.Email, "@", 2)
	domainPart := "billionmail"
	if len(domain) > 1 {
		domainPart = domain[1]
	}

	return fmt.Sprintf("<%d.%s@%s>", timestampMillis, randomID, domainPart)
}

// Send sends an email to specified recipients
func (e *EmailSender) Send(message Message, recipients []string) error {
	if len(recipients) == 0 {
		return fmt.Errorf("no recipients specified")
	}

	e.mutex.Lock()
	defer e.mutex.Unlock()

	// Make sure we have a connection
	if !e.connected || e.client == nil {
		e.mutex.Unlock()
		if err := e.Connect(); err != nil {
			e.mutex.Lock()
			return fmt.Errorf("failed to connect: %w", err)
		}
		e.mutex.Lock()
	}

	// Try to send the message, with reconnect on failure
	err := e.doSend(message, recipients)
	if err != nil {

View on GitHub (pinned to fc36c76c05)

Solutions

  1. Fix the caller (sendApiMailWithSender) to ensure recipients is populated before invoking Send, failing fast with a clearer upstream message.
  2. If sending to zero contacts is expected/legitimate, skip the Send call instead of invoking it.
  3. Validate and dedupe recipient addresses upstream so a partially valid list still sends to valid entries.
  4. Log the campaign/message context when this triggers to identify which upstream step dropped the recipients.

Example fix

// before
err := sender.Send(message, recipients)
// after
if len(recipients) == 0 {
    return fmt.Errorf("send skipped: no recipients resolved for this message")
}
err := sender.Send(message, recipients)
Defensive patterns

Strategy: validation

Validate before calling

func send(sender *EmailSender, msg Message, recipients []string) error {
    if len(recipients) == 0 {
        return fmt.Errorf("send skipped: no recipients resolved")
    }
    filtered := make([]string, 0, len(recipients))
    for _, r := range recipients {
        if addr, err := mail.ParseAddress(r); err == nil {
            filtered = append(filtered, addr.Address)
        }
    }
    if len(filtered) == 0 {
        return fmt.Errorf("send skipped: no valid recipient addresses")
    }
    return sender.Send(msg, filtered)
}

Type guard

func hasRecipients(recipients []string) bool {
    return len(recipients) > 0
}

Try / catch

if err := sender.Send(msg, recipients); err != nil {
    if err.Error() == "no recipients specified" {
        // caller bug, not a mail-server problem: log campaign context and skip
        log.Printf("skipped send: empty recipient list")
        return nil
    }
    return err
}

Prevention

When it happens

Trigger: Calling EmailSender.Send(message, recipients) with an empty slice — e.g. a campaign resolved to zero contacts, or a nil recipients value passed through sendApiMailWithSender.

Common situations: A campaign/contact query filtered everyone out before sending; an API caller sent an empty recipients array; a code path forgot to append CC/BCC into the recipients list; a bug mapping contact list to []string produced an empty result.

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 Billionmail/BillionMail@fc36c76c05 (2026-09-05). Data as JSON: /api/errors/5ab0a40175fff7f1. Report an issue: GitHub.