{"record":{"id":"5ab0a40175fff7f1","repo":"Billionmail/BillionMail","slug":"no-recipients-specified","errorCode":null,"errorMessage":"no recipients specified","messagePattern":"no recipients specified","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"core/internal/service/mail_service/sending.go","lineNumber":309,"sourceCode":"// GenerateMessageID generates a unique Message-ID for email\nfunc (e *EmailSender) GenerateMessageID() string {\n\trandomBytes := grand.B(16)\n\trandomID := hex.EncodeToString(randomBytes)\n\ttimestampMillis := time.Now().UnixMilli()\n\n\tdomain := strings.SplitN(e.Email, \"@\", 2)\n\tdomainPart := \"billionmail\"\n\tif len(domain) > 1 {\n\t\tdomainPart = domain[1]\n\t}\n\n\treturn fmt.Sprintf(\"<%d.%s@%s>\", timestampMillis, randomID, domainPart)\n}\n\n// Send sends an email to specified recipients\nfunc (e *EmailSender) Send(message Message, recipients []string) error {\n\tif len(recipients) == 0 {\n\t\treturn fmt.Errorf(\"no recipients specified\")\n\t}\n\n\te.mutex.Lock()\n\tdefer e.mutex.Unlock()\n\n\t// Make sure we have a connection\n\tif !e.connected || e.client == nil {\n\t\te.mutex.Unlock()\n\t\tif err := e.Connect(); err != nil {\n\t\t\te.mutex.Lock()\n\t\t\treturn fmt.Errorf(\"failed to connect: %w\", err)\n\t\t}\n\t\te.mutex.Lock()\n\t}\n\n\t// Try to send the message, with reconnect on failure\n\terr := e.doSend(message, recipients)\n\tif err != nil {","sourceCodeStart":291,"sourceCodeEnd":327,"githubUrl":"https://github.com/Billionmail/BillionMail/blob/fc36c76c050c3775c5e899faf7403cf0262d2744/core/internal/service/mail_service/sending.go#L291-L327","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Fix the caller (sendApiMailWithSender) to ensure recipients is populated before invoking Send, failing fast with a clearer upstream message.","If sending to zero contacts is expected/legitimate, skip the Send call instead of invoking it.","Validate and dedupe recipient addresses upstream so a partially valid list still sends to valid entries.","Log the campaign/message context when this triggers to identify which upstream step dropped the recipients."],"exampleFix":"// before\nerr := sender.Send(message, recipients)\n// after\nif len(recipients) == 0 {\n    return fmt.Errorf(\"send skipped: no recipients resolved for this message\")\n}\nerr := sender.Send(message, recipients)","handlingStrategy":"validation","validationCode":"func send(sender *EmailSender, msg Message, recipients []string) error {\n    if len(recipients) == 0 {\n        return fmt.Errorf(\"send skipped: no recipients resolved\")\n    }\n    filtered := make([]string, 0, len(recipients))\n    for _, r := range recipients {\n        if addr, err := mail.ParseAddress(r); err == nil {\n            filtered = append(filtered, addr.Address)\n        }\n    }\n    if len(filtered) == 0 {\n        return fmt.Errorf(\"send skipped: no valid recipient addresses\")\n    }\n    return sender.Send(msg, filtered)\n}","typeGuard":"func hasRecipients(recipients []string) bool {\n    return len(recipients) > 0\n}","tryCatchPattern":"if err := sender.Send(msg, recipients); err != nil {\n    if err.Error() == \"no recipients specified\" {\n        // caller bug, not a mail-server problem: log campaign context and skip\n        log.Printf(\"skipped send: empty recipient list\")\n        return nil\n    }\n    return err\n}","preventionTips":["Validate recipients upstream of Send; fail fast with context about which step emptied the list.","Deduplicate and syntax-check addresses with net/mail.ParseAddress before sending.","Treat zero resolved contacts as a business-level skip, not a Send() call."],"tags":["validation","smtp","input"],"backgroundTag":"missing-required-argument","analyzedSha":"fc36c76c050c3775c5e899faf7403cf0262d2744","analyzedAt":"2026-09-05T21:28:54.019Z","contentChangedAt":"2026-09-05T21:28:54.019Z","schemaVersion":2},"datasetVersion":"2026-09-12T22:17:10.623Z"}