Billionmail/BillionMail · error

failed to send alert email to %d recipient(s): %v

Error message

failed to send alert email to %d recipient(s): %v

What it means

sendAlertEmail iterates settings.RecipientList, sending to each recipient and recording addresses whose Send() call fails. If any recipient failed, it returns 'failed to send alert email to %d recipient(s): %v' listing the count and the failed addresses. Note the connection already succeeded — this is a per-recipient delivery failure, and the successful recipients still got the alert.

Source

Thrown at core/internal/service/domains/blacklist.go:507

	var failedRecipients []string
	for _, recipient := range settings.RecipientList {

		message := mail_service.NewMessage(subject, body)
		message.SetRealName(settings.Name)
		messageId := sender.GenerateMessageID()
		message.SetMessageID(messageId)

		err = sender.Send(message, []string{recipient})
		if err != nil {
			g.Log().Errorf(ctx, "Failed to send alert email to %s: %v", recipient, err)
			failedRecipients = append(failedRecipients, recipient)
		} else {
			g.Log().Infof(ctx, "Alert email sent successfully to %s", recipient)
		}
	}

	if len(failedRecipients) > 0 {
		return fmt.Errorf("failed to send alert email to %d recipient(s): %v", len(failedRecipients), failedRecipients)
	}

	return nil
}

func buildBlacklistAlertEmailHTML(ip, domain string, result *model.BlacklistCheckResult) string {

	var blacklistItems string
	for i, bl := range result.BlackList {
		blacklistItems += fmt.Sprintf("                <li><strong>%d. %s</strong> (Response: %s)</li>\n",
			i+1, bl.Blacklist, bl.Response)
	}

	return fmt.Sprintf(`
<!DOCTYPE html>
<html lang="en">
	<head>
		<meta charset="UTF-8" />

View on GitHub (pinned to fc36c76c05)

Solutions

  1. Review the failedRecipients list in the wrapped error and correct or remove invalid addresses from alert settings
  2. Check the SMTP server logs for the exact rejection codes (550/551/554) per failed address
  3. Ensure the sender domain has valid SPF/DKIM/DMARC so recipient servers accept the mail
  4. Resend the alert to only the failed recipients after fixing the cause

Example fix

// before
if len(failedRecipients) > 0 {
	return fmt.Errorf("failed to send alert email to %d recipient(s): %v", len(failedRecipients), failedRecipients)
}
// after
if len(failedRecipients) > 0 {
	return fmt.Errorf("failed to send alert email to %d/%d recipient(s): %v (subject=%q)", len(failedRecipients), len(settings.RecipientList), failedRecipients, subject)
}
Defensive patterns

Strategy: fallback

Validate before calling

// validate recipient addresses before sending
for _, r := range settings.RecipientList {
	if _, err := mail.ParseAddress(r); err != nil {
		return fmt.Errorf("invalid alert recipient %q: %w", r, err)
	}
}

Type guard

func validRecipients(rs []string) bool {
	for _, r := range rs {
		if _, err := mail.ParseAddress(r); err != nil {
			return false
		}
	}
	return len(rs) > 0
}

Try / catch

err := sendAlertEmail(ctx, settings, subject, body)
if err != nil && strings.Contains(err.Error(), "failed to send alert email to") {
	// partial failure: alert still reached some recipients;
	// parse failedRecipients from the message and retry only those
}

Prevention

When it happens

Trigger: Called from sendBlacklistAlert when one or more recipient addresses are rejected by the SMTP server (550 no such user, 554 relay denied, greylisting, invalid address format, mailbox full) during the per-recipient Send loop.

Common situations: Typo'd or departed employee addresses in RecipientList; recipients on domains with strict SPF/DMARC rejecting the sender; SMTP relay refusing to relay for external domains; rate limiting after many alert recipients; alias addresses that no longer exist in the alias table.

Related errors


AI-assisted analysis of Billionmail/BillionMail@fc36c76c05 (2026-09-05). Data as JSON: /api/errors/6027cabcab6d95c1. Report an issue: GitHub.